Started: 2020-09-25
Last edited: 2020-10-01 13:31:50

library(tidyverse)

# single cell
library(Seurat)

# plotting
library(patchwork)
library(ggthemes)
library(ggrepel)
library(ggforce) # for sina plots
library(cowplot)
library(ggtext)
library(ungeviz) # for geom_hpline

# go enrichment
library(clusterProfiler)
library(org.Hs.eg.db)
library(GO.db)

1 Background

Our goal here is to perform differential expression analysis between covid and control samples separately for each annotated cell type in the single cell RNA-seq data.

This analysis is the same as 05_de-by-celltype_logfc0.25.Rmd, except for the log fold change cutoff for DE analysis.

2 Data

We will use the Seurat object in which we have saved the cell annotations.

seur <- readRDS("../results/02_annotation/seurat-object_annotated.rds")

3 Differentialy expressed genes

We will use the “merged” annotations as our celltypes for which to perform DE analysis. For identifying differentially expressed genes, we will use Seurat::FindMarkers(). For this we first have to create a new metadata column with celltype_covid and celltype_control values, and then we run DE between these two idents. This function by dafault uses the Wilcoxon Rank Sum test, which we won’t change. By default this function outputs all genes for which abs(logFC) is at least 0.25, without filtering by p value. We will not use these defaults. Instead:

  1. Fold change cutoff: \(|log(Fold Change)| > 0.4\)
  2. \(p_{adj} < 0.05\)

Seurat::FindMarkers() uses natural log, not log2, and uses bonferroni adjustment to correct for multiple testing (see https://github.com/satijalab/seurat/issues/741). \(|log(FoldChange)| > 0.4\) corresponds to exp(0.4) = 1.49 or at least 50% change in expression.

The output of this for each cell type is stored as .csv in results/04_de-genes-by-celltype/logfc_0.40/de/files.

# set up
seur$celltype_covid <- paste(seur$annotation_merged, seur$covid, sep = "_")
Idents(seur) <- seur$celltype_covid
# de
de.genes <- list()

for (i in unique(seur$annotation_merged)) {
  de.genes[[i]] <-  FindMarkers(seur, 
                                ident.1 = paste0(i, "_covid"), 
                                ident.2 = paste0(i, "_cntrl"),
                                assay = "SCT", verbose = FALSE) %>% 
    dplyr::filter(p_val_adj < 0.05 & abs(avg_logFC) > 0.4) %>% 
    dplyr::rename(pct.covid = pct.1,
                  pct.cntrl = pct.2)
}
# write DE genes to files
for(i in unique(seur$annotation_merged)) {
  write.csv(
    de.genes[[i]],
    file = paste0("../results/04_de-genes-by-celltype/logfc_0.40/de/files/de-genes_", i, ".csv" ),
    row.names = TRUE)
}

3.1 Number of DE genes by celltype

de.number <- lapply(de.genes, nrow) %>% as.data.frame() %>% t() %>% as.data.frame()
colnames(de.number) <- "number"
de.number$celltype <- rownames(de.number)

de.number <- de.number[order(de.number$number, decreasing = TRUE), ]
de.number$celltype <- factor(de.number$celltype, levels = de.number$celltype)

p <- ggplot(data = de.number, aes(x = celltype, y = number, label = number)) +
  geom_bar(stat = "identity", fill = "grey50", width = 0.8) +
  geom_text(size = 1.8, vjust = -0.25) +
  ggtitle("Number of DE genes") +
  coord_cartesian(clip = "off") +
  theme_minimal() +
  theme(plot.title = element_text(size = 8),
        panel.grid.minor = element_blank(),
        panel.grid.major = element_line(size = 0.2),
        legend.key.size = unit(0.75, "lines"),
        axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, size = 6),
        axis.text.y = element_text(size  = 6),
        axis.title = element_text(size = 7))

ggsave(p, filename = "../results/04_de-genes-by-celltype/logfc_0.40/de/plots/number-of-de-genes.pdf", width = 3.5, height = 3, units = "in")

print(p)

3.2 Mitochondrial UMI

For some cell types a lot of mitochondrial genes are DE between covid and control. It’s possible that those genes are really differentially expressed, but it’s more likely that there is a difference between covid and control samples in terms of quality of cell-prep. We can look at percent mitochondrial UMI in all cell types by covid status to see if this is true.

ggplot(data = seur@meta.data, aes(x = covid, y = percent.mt)) +
  geom_sina(aes(color = covid), size = 1, alpha = 1) +
  scale_color_tableau() +
  facet_grid(.~ annotation_merged) +
  labs(caption = "% mitochondrial reads in all cell types") +
  theme_minimal() +
  theme(axis.text.x = element_blank(),
        axis.title.x = element_blank(),
        panel.grid.major = element_line(size = 0.2),
        panel.grid.minor = element_blank(),
        strip.text.x = element_text(angle = 90, hjust = 0, vjust = 0.5)) 

For most cell types there appears to be a difference in the distribution of percent mitochondrial reads between covid and control samples. Covid samples generally have lower % mitochondrial reads. Next, we can plot the absolute value of the difference in percent mitochondrial reads between covid and control against the number of DE genes that are mitochondrial. If the mitochondrial genes in DE analysis are driven by differences in sample quality, we should expect a positive relationship.

mt <- data.frame(
  celltype = sort(names(de.genes)),
  mt.diff = NA,
  mt.de = NA
)

for(i in mt$celltype) {
  mt$mt.diff[mt$celltype == i] <- abs(
    median(seur$percent.mt[seur$annotation_merged == i & seur$covid == "covid"]) -
      median(seur$percent.mt[seur$annotation_merged == i & seur$covid == "cntrl"])
  )
  
  mt$mt.de[mt$celltype == i] <- grep("MT-", rownames(de.genes[[i]])) %>% length()
}
ggplot(data = mt, aes(x = mt.diff, y = mt.de, label = celltype)) +
  geom_point(shape = 21, color = "black", fill = "grey40", size = 3) +
  coord_cartesian(xlim = c(0, NA), ylim = c(0, NA)) +
  geom_text_repel(size = 3, color = "grey40") +
  labs(x = "\nabs(median(% mt in covid) - median(% mt in control))",
       y = "number of DE mitochondrial genes") +
  theme_minimal() +
  theme(panel.grid.major = element_line(size = 0.25),
        panel.grid.minor = element_blank())

There is a very mild positive relationship, more so for villous cell types. Three decidual cell types—dec.Mono_1, dec.Mono_2, and dec.Tcell_2—have high number of MT DE genes despite low difference in percent MT reads.

We can ignore the mitochondrial genes since the number of genes is quite small compared to the total number of DE genes.

4 Interferome

Interferome (Rusinova et al. 2012) is a manually curated database of interferon-responsive genes, available at http://www.interferome.org/interferome/home.jspx. The database essentially contains a list of all genes whose expression is affected by interferons, as inferred from various datasets. These datasets include in vitro and in vivo studies from human and mouse. The online interface includes a search function which allows users to input a list of genes, which are searched against the database. The output includes various things including: (1) list of genes from the input that are found in the inferferome database, i.e. there is evidence for these genes to be modulated by interferon, (2) a detailed table with links pointing to the dataset from which the evidence was gathered for each gene, (3) gene ontology enrichment, (4) TF binding site analysis, etc.

I ran the list of DE genes for each of the 21 celltypes through Interferome (version 2.01, 2020-09-20) and saved the text output for (1) from above. I used the following search parameters (all defaults except for narrowing species to Homo sapiens)

Interferome Type:   Any
Interferome SubType:    Any
Treatment Concentration:    Any
Treatment Time: Any
Vivo/Vitro: Any
Species:    Homo sapiens
System: Any
Organ:  Any
Cell:   Any
Cell Line:  Any
Normal/Abnormal:    Any
Fold Change Up: 2.0
Fold Change Down:   2.0

4.1 Fraction of DE in interferome database

We want to calculate for each celltype, the fraction of DE genes (between covid and control) which are known to be interferon-responsive. For this, below we will read the Interferome output text files and calculate that fraction.

# read interferome output
ifome <- list()

for(i in names(de.genes)) {
  ifome[[i]] <- read.table(
    paste0(
      "../results/04_de-genes-by-celltype/logfc_0.40/interferome/files/interferome_", 
      i, ".txt"), 
    header = TRUE, sep = "\t", quote = "", skip = 19, 
    strip.white = TRUE, stringsAsFactors = FALSE)
}
# create a dataframe of number of genes in interferome for each cell type
ifome.n <- data.frame(
  celltype = names(de.genes),
  n_de = NA,
  n_interferome.yes = NA
)

# calculate number of genes
for (i in names(de.genes)) {
  
  ifome.n$n_de[ifome.n$celltype == i] <- nrow(de.genes[[i]])
  
  ifome.n$n_interferome.yes[ifome.n$celltype == i] <- 
    (rownames(de.genes[[i]]) %in% ifome[[i]]$Gene.Name) %>% 
    sum()

}

ifome.n$n_interferome.no <- ifome.n$n_de - ifome.n$n_interferome.yes

# calculate percentages
ifome.n$pct_interferome.yes <- (ifome.n$n_interferome.yes/ifome.n$n_de) * 100
ifome.n$pct_interferome.no <- 100 - ifome.n$pct_interferome.yes
# prepare for plotting
# melt
ifome.n.long <- pivot_longer(data = ifome.n,
                             cols = 3:6,
                             names_to = c(".value", "interferome"), 
                             names_pattern = "(.+)_(.+)")
ifome.n.long$interferome <- gsub("interferome.", "", ifome.n.long$interferome)

ifome.n.long$celltype <- factor(ifome.n.long$celltype, 
                                levels = de.number$celltype)
# plot iterferome output

theme2 <-   theme_classic() +
  theme(
    legend.key.size = unit(0.5, "lines"),
    legend.title = element_text(size = 6),
    legend.text = element_text(size = 5.5),
    axis.text.y = element_text(angle = 0, hjust = 1, vjust = 0.5, size = 6, 
                               color = "black"),
    axis.text.x = element_text(size = 6, color = "black"),
    axis.title = element_text(size = 7),
    axis.title.y = element_blank(),
    axis.line = element_line(size = 0.25),
    axis.ticks.y = element_line(size = 0.25),
    axis.ticks.x = element_line(size = 0.25))

p.n <- ggplot(data = ifome.n.long, 
              aes(y = celltype, x = n)) +
  geom_bar(position = "stack", stat = "identity", 
           aes(fill = interferome), width = 0.8) +
  scale_fill_manual(values = c("grey90", "#4e79a7"), 
                    breaks = c("no", "yes")) +
  scale_y_discrete(expand = c(0.05, 0)) +
  scale_x_continuous(expand = c(0.025, 0)) +
  geom_text(data = ifome.n.long[ifome.n.long$interferome == "yes", ], 
            aes(y = celltype, x = n, label = n),
            size = 1.8, vjust = 0.5, hjust = 0.0, nudge_x = 1) +
  labs(x = "number") +
  theme2 +
  theme(legend.position = c(0.90, 0.90))

p.pct <- ggplot(data = ifome.n.long, 
                aes(y = celltype, x = pct)) +
  geom_bar(position = "stack", stat = "identity", 
           aes(fill = interferome), width = 0.8) +
  scale_fill_manual(values = c("grey90", "#4e79a7"), 
                    breaks = c("no", "yes")) +
  scale_y_discrete(expand = c(0.05, 0)) +
  scale_x_continuous(expand = c(0.025, 0)) +
  geom_text(data = ifome.n.long[ifome.n.long$interferome == "yes", ], 
            aes(y = celltype, x = pct, label = round(pct, 0)),
            size = 1.8, vjust = 0.5, hjust = 0.0, nudge_x = 0.5) +
  labs(x = "percent") +
  theme2 +
  theme(legend.position = "none")


plot_row <- plot_grid(p.n, p.pct)

# now add the title
title <- ggdraw() +
  draw_label("DE genes in interferome database", x = 0, hjust = 0, size = 8) +
  theme(
    # add margin on the left of the drawing canvas,
    # so title is aligned with left edge of first plot
    plot.margin = margin(0, 0, 0, 7)
  )

p <- plot_grid(title, plot_row,
               ncol = 1,
               # rel_heights values control vertical title margins
               rel_heights = c(0.05, 1)
)

ggsave(p,
       filename = "../results/04_de-genes-by-celltype/logfc_0.40/interferome/plots/de-genes-interferome.pdf",
       width = 6, height = 3, units = "in")

print(p)

4.2 Test for enrichment

For most cell types, a large chunk of genes differentially expressed between covid and control are in fact known to be responsive to interferons according to Interferome. To find out whether this fraction is larger than what one would expect by chance, we can do a hypergeometric test for enrichment, similar to the one that we do for GO enrichment.

If \(N\) is the total number of genes used in DE analysis, \(B\) is the number of genes out of \(N\) that are in the interferome database, \(n\) is the number of DE genes for a cell type, and \(b\) is the number of genes genes out of \(n\) that are in the interferome database, then: \(enrichment = \frac{b}{n}/\frac{B}{N}\). And the hypergeometric distribution, we can calculate the p value (the cumulative probability of finding \(b\) or more genes in a sample of \(n\) DE genes, given \(B\) and \(N\)).

# total number of genes in the dataset
ifome.n$N <- nrow(seur@assays$SCT@data)

# total number of genes in the interferome dataset
ifome.n$B <- 12614

# enrichment
ifome.n$enrichment <- (ifome.n$n_interferome.yes/ ifome.n$n_de)/
  (ifome.n$B/ifome.n$N)

# p value
#phyper(q = 244-1, m = 12614, n = 23502-12614, k = 355, lower.tail = FALSE)

ifome.n$pval <- phyper(
  q = ifome.n$n_interferome.yes - 1,
  m = ifome.n$B,
  n = ifome.n$N - ifome.n$B,
  k = ifome.n$n_de,
  lower.tail = FALSE
)

# colors for plot
ifome.n$color <- ifelse(ifome.n$pval < 0.05, yes = "Black", no = "Grey")
# write to file
ifome.n %>% 
  dplyr::rename("n_total.genes" = "N", "n_interferome.genes" = "B") %>% 
  dplyr::select(-"color") %>% 
  write.csv(., "../results/04_de-genes-by-celltype/logfc_0.40/interferome/files/number-of-DE-genes-in-interferome.csv", 
            row.names = FALSE)
ifome.n[, c(1, 2, 3, 7, 8, 9, 10)] %>% knitr::kable()
celltype n_de n_interferome.yes N B enrichment pval
dec.DSC 355 244 23502 12614 1.280600 0.0000000
dec.SMC 276 204 23502 12614 1.377124 0.0000000
dec.APC 267 229 23502 12614 1.597998 0.0000000
vil.FB 161 133 23502 12614 1.539139 0.0000000
dec.Endo 263 191 23502 12614 1.353099 0.0000000
vil.Hofb 131 107 23502 12614 1.521824 0.0000000
dec.NK_1 348 261 23502 12614 1.397376 0.0000000
vil.EVT 61 40 23502 12614 1.221749 0.0401057
dec.FB 193 155 23502 12614 1.496327 0.0000000
vil.VCT 118 100 23502 12614 1.578956 0.0000000
dec.Tcell_1 349 253 23502 12614 1.350663 0.0000000
dec.Mono_1 176 125 23502 12614 1.323273 0.0000017
dec.Tcell_2 83 61 23502 12614 1.369316 0.0001628
dec.NK_2 276 206 23502 12614 1.390625 0.0000000
dec.Bcells 23 15 23502 12614 1.215110 0.1840788
vil.Ery 232 161 23502 12614 1.292974 0.0000007
dec.NK_3 282 201 23502 12614 1.328003 0.0000000
dec.Tcell_3 116 74 23502 12614 1.188573 0.0173755
dec.Mono_2 112 91 23502 12614 1.513824 0.0000000
dec.Gran 5 0 23502 12614 0.000000 1.0000000
vil.SCT 23 13 23502 12614 1.053095 0.4762411
# Plot fraction of DE genes in interferome, along with enrichment p values.

plot.dat <- ifome.n.long
plot.dat$celltype <- factor(
  plot.dat$celltype, 
  levels = ifome.n$celltype[order(ifome.n$pct_interferome.yes)]
  )
p <- ggplot(data = plot.dat, 
            aes(y = celltype, x = pct)) +
  geom_bar(position = "stack", 
           stat = "identity", 
           aes(fill = interferome), 
           width = 0.8) +
  scale_fill_manual(values = c("grey90", "#4e79a7"), 
                    breaks = c("no", "yes"),
                    name = "Interferome", 
                    labels = c("No", "Yes")) +
  scale_y_discrete(expand = c(0.05, 0)) +
  scale_x_continuous(expand = c(0.025, 0)) +
  coord_cartesian(clip = "off") +
  geom_text(data = plot.dat[plot.dat$interferome == "yes", ], 
            aes(y = celltype, x = pct, 
                label = paste0(n, "/", n_de)),
            size = 1.8, vjust = 0.5, hjust = 0.0, nudge_x = 0.5) +
  labs(x = "DE genes present in Interferome (%)") +
  geom_text(data = ifome.n[order(levels(plot.dat$celltype)), ], 
            aes(y = celltype, x = 100, 
                label = formatC(pval, format = "e", digits = 0)),
            hjust = 0, size = 1.75, nudge_x = 1,
            color = ifome.n$color[order(levels(plot.dat$celltype))]) +
  annotate(geom = "richtext", x  = 101, y = 22, size = 2,
           label = "enrichment *p* value", hjust = 0,
           fill = NA, label.color = NA, # remove background and outline
           label.padding = grid::unit(0, "pt")) + # remove padding
  theme2 +
  theme(legend.position = "right",
        legend.margin = margin(0, 0, 0, 3))

ggsave(p, filename = "../results/04_de-genes-by-celltype/logfc_0.40/interferome/plots/interferome_percent_enrichment-pval.pdf", width = 3.5, height = 3, units = "in")

print(p)

Even though the enrichment is modest, about 1.5 fold, for most cell types, it is highly unlikely by chance. The p values for most cell types are almost zero. Only three cell types have p values higher than 0.05.

5 GO enrichment

I also ran GO enrichment (only for the Biological Process category) for DE genes (only those that are up-regulated in covid) in all celltypes. The table outputs for all celltypes are saved as .csv in results/04_de-genes-by-celltype/logfc_0.40/go/files. For most cell types, the enriched GO categproes include those related to defense to viral infection, interferon response genes, misfolded protein response, etc. See below for the top 10 enriched GO categories for DE genes in each celltype.

# function for GO enrichment on upregulated genes for each cell type
enrichGO2 <- function(celltype, ...) {
  
  up <- de.genes[[celltype]] %>% 
    dplyr::filter(avg_logFC > 0)
  
  ids.bitr <- bitr(rownames(up), 
                   fromType = "SYMBOL", toType = c("ENTREZID", "SYMBOL"), 
                   OrgDb = "org.Hs.eg.db")
    
  features <- ids.bitr$ENTREZID

  ego <- enrichGO(
    gene          = features,    
    OrgDb         = org.Hs.eg.db,
    ont           = "BP",
    pAdjustMethod = "BH",
    pvalueCutoff  = 0.01, # default 0.01
    qvalueCutoff  = 0.05, # default 0.05
    readable = TRUE,
    ...)
  
  dftoprint <- clusterProfiler::simplify(ego)@result
  rownames(dftoprint) <- NULL
  
  return(dftoprint)
}
# perform GO enrichment
de.go <- list()

for(i in names(de.genes)) {
  de.go[[i]] <- enrichGO2(celltype = i)
}
'select()' returned 1:1 mapping between keys and columns
2.09% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
1.93% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
0.81% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
2.46% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
1.27% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
'select()' returned 1:1 mapping between keys and columns
2.91% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
'select()' returned 1:1 mapping between keys and columns
2% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
1.89% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
4.17% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
2.38% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
3.57% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
3.38% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
'select()' returned 1:1 mapping between keys and columns
4.76% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
3.03% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
4.31% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
2.5% of input gene IDs are fail to map...--> No gene can be mapped....
--> Expected input gene ID: 51668,7455,1017,23493,7486,5266
--> return NULL...
Error in (function (classes, fdef, mtable)  : 
  unable to find an inherited method for function ‘simplify’ for signature ‘"NULL"’
# write GO enrichments to files
for(i in unique(names(de.go))) {
  write.csv(
    de.go[[i]],
    file = paste0("../results/04_de-genes-by-celltype/logfc_0.40/go/files/enrichedGO_", i, ".csv" ),
    row.names = FALSE)
}
for(i in names(de.go)) {
  print(i)
  print(as.data.frame(de.go[[i]][1:10, 2]))
}
[1] "dec.DSC"
[1] "dec.SMC"
[1] "dec.APC"
[1] "vil.FB"
[1] "dec.Endo"
[1] "vil.Hofb"
[1] "dec.NK_1"
[1] "vil.EVT"
[1] "dec.FB"
[1] "vil.VCT"
[1] "dec.Tcell_1"
[1] "dec.Mono_1"
[1] "dec.Tcell_2"
[1] "dec.NK_2"
[1] "dec.Bcells"
[1] "vil.Ery"
[1] "dec.NK_3"
[1] "dec.Tcell_3"
[1] "dec.Mono_2"

6 Volcano plots

Below are volcano plots for all cell types: -log10(p value) vs logFC; top 30 genes with highest abs(logFC) are labeled.

# function for volcano plot
de_volcano <- function(celltype) {
  
  plot.dat <- de.genes[[celltype]]
  plot.dat$gene <- rownames(plot.dat)
  
  # top DE genes
  genes.to.label = de.genes[[celltype]] %>% 
    .[order(abs(de.genes[[celltype]]$avg_logFC), decreasing = TRUE), ] %>% 
    rownames() %>% .[1:30]
  
  plot.dat$label <- ifelse(test = plot.dat$gene %in% genes.to.label,
                           yes = plot.dat$gene,
                           no = NA)
  
  p <- ggplot(plot.dat, 
              aes(avg_logFC, -log10(p_val_adj), label = label)) + 
    geom_point(alpha = 0.4, size = 2) + 
    ggtitle(celltype) +
    geom_label_repel(size = 1.9, force = 1, color = "grey20") +
    theme_bw() +
    theme(panel.grid.major = element_line(size = 0.25),
          panel.grid.minor = element_blank(),
          axis.title = element_text(size = 7),
          axis.text = element_text(size = 7),
          plot.title = element_text(size = 8)) 
  
  return(p)
}
for(i in names(de.genes)) {
  p <- de_volcano(celltype = i)
  
  ggsave(
    p,
    filename = paste0(
      "../results/04_de-genes-by-celltype/logfc_0.40/de/plots/volcanoplot_", 
      i, ".pdf"),
    width = 5, height = 4, units = "in")
  
  print(p)
}

7 Labor signature

The control samples in our data come from C-sections. Which makes this a confounding factor in our analyses. In order to address this, we can compare the list of DE genes between covid and control samples with the list of DE genes between term in labor (TIL) and term not in labor (TNL). This can tell us if (and how much) part of our DE results are driven by labor rather than covid status.

The TIL vs TNL differential gene expression results from (Pique-Regi et al. 2019) can be used for this. I downloaded the supplementary file. The file is described as follows: “The columns represent: 1) Cluster or cell-type name, 2) Comparison groups or contrast (i.e., TNL vs TIL, TIL vs PTL), 3) Ensembl gene identifier, 4) Gene symbol, 5) baseMean gene baseline expression as calculated by DESeq2, 6) log2 Fold Change of the first group in column two versus the second group, 7) Standard error estimated for the log2 Fold Change, 8) Nominal p-value, 9) q-value or adjusted p-value to control for FDR. Only rows with q < 0.2 are reported.”.

til <- readxl::read_excel("../info/from-papers/pique-regi_2019/supp/elife-52004-supp5-v2.xls")
labor <- til %>% filter(Compare == "TNL_v_TIL" & padj < 0.05)
head(labor)

Now we have to find out how many of the DE genes between TNL and TIL are shared with DE genes between covid and control.

# all DE genes across all cell types between covid and control
de.all <- de.genes

for(i in names(de.all)) {
  de.all[[i]]$gene <- rownames(de.all[[i]])
}

de.all <- do.call(rbind, de.all)

de.all.genes <- de.all$gene %>% unique()

# all labor DE genes
labor.genes <- labor$Gene %>% unique()
# number of covid vs control DE genes (across all cell types)
length(de.all.genes)
[1] 1267
# number of TNL vs TIL DE genes (across all cell types)
length(labor.genes)
[1] 195
# number of DE genes shared between covid vs control and TNL vs TIL
length(intersect(de.all.genes, labor.genes))
[1] 84

There are 84 genes that are shared between covid vs control and TNL vs TIL. These are likely related to labor. These genes are as follows:

intersect(de.all.genes, labor.genes) %>% sort()
 [1] "ABCA1"    "ACTB"     "AHR"      "ANXA1"    "B4GALT1"  "C1QA"     "C1QB"    
 [8] "C1QC"     "CCL18"    "CCND2"    "CCNL1"    "CD163"    "CD2"      "CD248"   
[15] "CD3E"     "CD55"     "CDKN2A"   "CFLAR"    "CKLF"     "CLDN5"    "CXCL1"   
[22] "DEFB1"    "DNAJA4"   "ELF1"     "ELF3"     "EMP1"     "ETS2"     "FABP5"   
[29] "FCGR2B"   "FILIP1L"  "FNBP1"    "FNIP2"    "FOSB"     "GNLY"     "GPCPD1"  
[36] "HLA-DRB5" "HSD11B1"  "HSPA6"    "ICAM1"    "ID2"      "IDH2"     "IFI27"   
[43] "IFITM2"   "IFITM3"   "IGFBP1"   "IGFBP2"   "IGFBP4"   "IGFBP5"   "IL1RL1"  
[50] "JUND"     "KCNQ1OT1" "KDM6B"    "KLF6"     "LMNA"     "LSP1"     "MAFF"    
[57] "MALAT1"   "MBNL1"    "MGST1"    "NAMPT"    "NEAT1"    "NFKB1"    "NFKBIA"  
[64] "PDE4B"    "PNRC1"    "POU2F2"   "PTGDS"    "RASGEF1B" "RGCC"     "RPS26"   
[71] "SAMSN1"   "SERPINE1" "SOD2"     "SPP1"     "SRGN"     "SRSF5"    "STAB1"   
[78] "TFPI"     "TIPARP"   "TNFAIP2"  "TNFAIP3"  "TXN"      "TYMS"     "ZFP36"   

Now we can compare whether the shared genes change in the same direction that we expect based on the information that our control samples come from C-sections.

# create a dataframe of shared genes and their direction of changes
shared <- data.frame(
  gene = intersect(de.all.genes, labor.genes) %>% sort()
)

shared$labor_logfc <- NA
for(i in 1:nrow(shared)){
  shared$labor_logfc[i] <- mean(labor$log2FoldChange[labor$Gene == shared$gene[i]])
} # because the same gene can be DE in multiple cell types, we have to take average logFC. Not the best option, but quick and dirty. 

shared$covid_logfc <- NA
for(i in 1:nrow(shared)){
  shared$covid_logfc[i] <- mean(de.all$avg_logFC[de.all$gene == shared$gene[i]])
} 

shared$labor_pattern[shared$labor_logfc < 0] <- "TIL_high"
shared$labor_pattern[shared$labor_logfc > 0] <- "TNL_high"

shared$covid_pattern[shared$covid_logfc < 0] <- "control_high"
shared$covid_pattern[shared$covid_logfc > 0] <- "covid_high"

The contigency table of shared DE genes looks like this:

table(shared$labor_pattern, shared$covid_pattern)
          
           control_high covid_high
  TIL_high            9         59
  TNL_high            3         13

Or in terms of proportions, like this:

table(shared$labor_pattern, shared$covid_pattern) %>% prop.table()
          
           control_high covid_high
  TIL_high   0.10714286 0.70238095
  TNL_high   0.03571429 0.15476190

Fisher’s exact test for this contingency table looks like this:

table(shared$labor_pattern, shared$covid_pattern) %>% fisher.test(alternative = "two.sided")

    Fisher's Exact Test for Count Data

data:  .
p-value = 0.6912
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
 0.1383802 4.3366594
sample estimates:
odds ratio 
  0.664588 

Labor is likely a confounding factor in our analysis, but the effect is very mild, if any. Since our control samples are from C-sections (i.e. not in labor), but covid samples are at term and in labor (not C-section), if labor is a confounding factor, we should expect a high proportion of genes to be shared between “covid_high” and “TIL_high”. We see this looking at the odds ratio only. But the p value for Fisher’s exact test is 0.69, suggesting that the labor and covid status are largely independent in our samples.

While it’s nice to be aware of this potential but mild effect, it is not a big problem. This effect is only relevant for genes that are shared between the two comparisons—TNL vs TIL and covid vs control. That fraction of genes, to begin with, is small. Around 195 genes are DE between TIL and TNL. Of these 84 are also DE between covid and control, likely representing the “labor signature” rather than “covid signature”. But the number of genes that are DE between covid and control is much larger (1267), i.e. over 1000 genes are DE between covid and control that are not DE between TNL and TIL. These most likely represent the “covid signature” that we are interested in.

8 Data-vis ideas for the paper

8.1 DE genes dotplot

We need to find a good way to present DE genes. Violin plots are nice if the number of to show is small. If we want to show a small set of genes, we can use violin plots.

Another option is dotplots. The advantage of dotplots is that they are much more space-efficient, which is desirable for paper figures. In a 6inx8in figure, we can comfortably show data for about 50 genes in all cell types separated by covid status.

The Seurat::DotPlot function has some problems, though, that make is not really usable for good quality plotting. One big problem is how it handles splitting samples by treatment and control (covid and control in our case) in split.by argument. It does split your cell types by treatment status so that the average expression by treatment status for each cluster can be visualized, but it uses two different colours for control and treatment. This causes two problems. First, ggplot doesn’t support multiple colour scales, and Seurat, instead of finding a way around the issue, has chosen to not show the colourbar guide at all when split.by is used (for more on this and another related bug, see https://github.com/satijalab/seurat/issues/2487 and https://github.com/satijalab/seurat/issues/2342.). Second, using two colors for values (avg.exp) on the same scale is a bad design choice, especially when the point is to compare them side by side—intensities for two colours mapping to the exact same avg.exp value can be perceived differently just because they are two different colors.

  1. We have to do a work-around to plot the dotplots split by covid status, plotting with a single color. We just use ggplot directly to make the dotplots, with covid status on one axis, genes on another, and faceted by cell type. This requires us to do the calculations for avg.exp.scaled and pct.exp ourselves. For this we can cheat—we can run Seurat::dotplot with celltype_covid as idents so it calculate those values split by covid status for each cell type, but instead of plotting it, we can just extract the $data slot from the resulting ggplot object and use it for our custom plotting.
  2. We can also show result of DE analysis on top of this plot. We can add a line segment connecting the covid and control dots in those cell types in which the p value of DE test of below threshold.
  3. For gene order, we can use hclust for clustering, and use that order for sorting genes. This way, genes that have similar expression patterns are positioned close to each other, and there is some structure to the plotting order.

Here for demo, we’ll use top 50 DE genes from dec.APC.

# theme for DE genes faceted dotplot
theme_dotplot <- theme(
  panel.background = element_blank(),
  text = element_text(color = "Black"),
  panel.grid = element_blank(),
  panel.border = element_rect(fill = NA, size = 0.1, color = "grey60"),
  axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, size = 6, color = "Black"),
  axis.text.y = element_text(size = 6, color = "Black"),
  axis.title = element_blank(),
  axis.ticks = element_line(size = 0.1, color = "Black"),
  strip.text.x = element_text(angle = 90, hjust = 0, vjust = 0.5, size = 6.5, color = "Black"),
  strip.background = element_blank(),
  legend.title = element_text(size = 6),
  legend.position = "right",
  legend.box.spacing = unit(0.1, "lines"),
  legend.background = element_blank(),
  legend.title.align = 0,
  legend.text = element_text(size = 5.5),
  legend.key.size = unit(0.5, "lines"),
  legend.key = element_blank(),
  legend.spacing.y = unit(0.1, "lines"),
  panel.spacing.x = unit(0, "lines")
)
# extract plot data from Seurat::DotPlot
Idents(seur) <- seur$celltype_covid
p <- Seurat::DotPlot(object = seur, assay = "SCT", 
                     features = rownames(de.genes$dec.APC)[1:50])

p.dat <- p$data

p.dat$celltype <- gsub(p.dat$id, pattern = "(.+)_([a-z]{5})", replacement = "\\1")
p.dat$covid <- gsub(p.dat$id, pattern = "(.+)_([a-z]{5})", replacement = "\\2")
# clustering for ordering
cdat <- pivot_wider(data =  p.dat, id_cols = 3, names_from = id, values_from = avg.exp.scaled)

cdat <- as.data.frame(cdat)
rownames(cdat) <- cdat$features.plot
cdat$features.plot <- NULL

cor.matrix <- cor(t(cdat))

# reorder correlation matrix based on clustering
dd <- as.dist((1 - cor.matrix)) 
hc <- hclust(dd, method = 'complete')
cdat  <- cdat[hc$order, ]

p.dat$features.plot <- factor(p.dat$features.plot, levels = rownames(cdat))
# prepare annotation data to display DE stats. This gives a df of only the combination of celltypes and genes where adj.p < 0.05

de.ann <- de.genes

for(i in names(de.ann)) {
  de.ann[[i]]$gene <- rownames(de.ann[[i]])
  de.ann[[i]]$celltype <- i
} # add gene names as column. the celltype info too, so that we can rbind all list items.

de.ann <- do.call(rbind, de.ann)
de.ann <- subset(de.ann, gene %in% p.dat$features.plot)
de.ann$diff.exp <- de.ann$p_val_adj < 0.05
de.ann$diff.exp <- tolower(as.character(de.ann$diff.exp))

de.ann$p_val <- NULL
de.ann$avg_logFC <- NULL
de.ann$pct.covid <- NULL
de.ann$pct.cntrl <- NULL
rownames(de.ann) <- NULL
q <- ggplot(data = p.dat, aes(x = covid, y = features.plot)) +
  geom_point(aes(fill = avg.exp.scaled, size = pct.exp), shape = 21, stroke = 0, 
             color = "White") +
  scale_fill_gradient(low = "White", high = "Red", name = "avg.exp\n(scaled)") +
  scale_x_discrete(breaks = c("cntrl", "covid"), 
                   labels = c("Control", "Covid")) +
  scale_size_area(max_size = 3.5,
                  guide = guide_legend(
                    override.aes = list(
                      color = "White", 
                      fill = "Black"))
                  ) +
  facet_grid(. ~ celltype) +
  ungeviz::geom_hpline(data = de.ann,
                       aes(y = gene, x = 1.5, color = diff.exp),
                       size = 0.35, width = 1, lineend = "butt") +
  scale_color_manual(breaks = "true",
                     labels = "< 0.05",
                     values = "Black",
                     name = "DE adj.p") +
  theme_dotplot


cowplot::ggsave2(q, 
                 filename = "../results/04_de-genes-by-celltype/logfc_0.40/de/plots/dotplot_top50-dec.APC.pdf", 
                 width = 6, height = 7, units = "in")

print(q)

8.2 GO enrichment dotplot

We also have to find a way to visualize the GO enrichment results. This one is tricky, but dotplot of enrichment and p value is a good option. But there are too many GO categories that enriched across all celltypes (around 1000), so we do not to filter them somehow.

8.2.1 Calculate enrichment

First we put all GO enrichment results in the same dataframe and then calculate the enrichment. Here the GO categories will be repeated because the same GO category can be enriched in multiple cell types, but they will of course have different enrichment and p values in each cell type.

# prepare data
go.pdat <- de.go[lapply(de.go, nrow) > 0]

for(i in names(go.pdat)) {
  go.pdat[[i]]$celltype <- i
}

go.pdat <- do.call(rbind, go.pdat)

go.pdat$pvalue <- NULL
go.pdat$geneID <- NULL
rownames(go.pdat) <- NULL

# calculate enrichment
go.pdat$b <- as.numeric(sapply(
    strsplit(go.pdat$GeneRatio, split = "/"), MARGIN = 1, FUN = '[['))

go.pdat$n <- as.numeric(sapply(
    strsplit(go.pdat$GeneRatio, split = "/"), MARGIN = 2, FUN = '[['))

go.pdat$B <- as.numeric(sapply(
    strsplit(go.pdat$BgRatio, split = "/"), MARGIN = 1, FUN = '[['))

go.pdat$N <- as.numeric(sapply(
    strsplit(go.pdat$BgRatio, split = "/"), MARGIN = 2, FUN = '[['))

go.pdat$enrichment <- (go.pdat$b / go.pdat$n) / (go.pdat$B / go.pdat$N)

head(go.pdat)

8.2.2 Plotting function

Since the same GO categories can be enriched in different celltypes, to avoid repetition and to enable seeing patterns of enrichment across celltypes, we want to visualize enrichment output not individually by celltype, but in a combined plot for all celltypes. We can dotplot celltype on one axis, and GO category on another. Color of the dot represents p value and size represents enrichment. We can cluster the GO categories by and order them accordingly so that we can easily see the patterns across celltypes.

# theme for GO dotplot
theme_GO <- theme(
  panel.background = element_blank(),
  text = element_text(color = "Black"),
  panel.grid.major = element_line(size = 0.15, color = "Grey90"),
  panel.border = element_blank(),
  axis.line = element_line(size = 0.25, color = "Black"),
  axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, size = 6, color = "Black"),
  axis.text.y = element_text(size = 6, color = "Black"),
  axis.title = element_blank(),
  axis.ticks = element_line(size = 0.1, color = "Black"),
  strip.text.x = element_text(angle = 90, hjust = 0, vjust = 0.5, size = 6.5, color = "Black"),
  legend.title = element_text(size = 6),
  legend.direction = "vertical",
  legend.box = "vertical",
  legend.box.spacing = unit(0.2, "lines"),
  legend.box.just = "left",
  legend.position = "right",
  legend.background = element_blank(),
  legend.title.align = 0,
  legend.text = element_text(size = 5.5),
  legend.key.size = unit(0.5, "lines"),
  legend.key = element_blank(),
  legend.spacing.y = unit(0.2, "lines"),
)
# function

dotplot_go <- function(go.pdat.sub) {
  
  # clustering for ordering
  cdat <- pivot_wider(data = go.pdat.sub, id_cols = 2, 
                      names_from = celltype, values_from = p.adjust)
  cdat[, 2:ncol(cdat)] <- apply(cdat[, 2:ncol(cdat)], 
                                MARGIN = 2, FUN = function(x){replace_na(x, 1)})
  
  cdat <- as.data.frame(cdat)
  rownames(cdat) <- cdat$Description
  cdat$Description <- NULL
  
  cor.matrix <- cor(t(cdat))
  
  dd <- as.dist((1 - cor.matrix)) 
  hc <- hclust(dd, method = 'complete')
  
  cdat <- cdat[hc$order, ]
  
  go.pdat.sub$Description <- factor(go.pdat.sub$Description, 
                                    levels = rownames(cdat))
  # plot
  p <- ggplot(data = go.pdat.sub,
              aes(x = celltype, y = Description)) +
    geom_point(aes(size = enrichment, color = -log10(p.adjust))) +
    scale_color_gradient(low = colorRampPalette(c("White", "Blue"))(10)[5],
                         high = colorRampPalette(c("White", "Blue"))(10)[10]) +
    scale_size_area(max_size = 3.5) +
    theme_GO
  
  return(p)
}

8.2.3 Dotplot without redundancy filtering

Here, after having combined all enriched GO categories from all cell types, we sort them by p value and pick the top 100, regardless of which cell type they come from. This is perhaps not the best way to filter, but one option that picks up the most highly enriched categories across most celltypes. But it does miss some celltypes whose enriched GO categories are not in the top 100.

# plotting subset
go.sub <- go.pdat[order(go.pdat$p.adjust, decreasing = FALSE)[1:100], ]

p <- dotplot_go(go.pdat.sub = go.sub)

ggsave(
  p, 
  filename = "../results/04_de-genes-by-celltype/logfc_0.40/go/plots/go_dotplot_top100.pdf",
  width = 5, height = 5, units = "in"
)

print(p)

8.2.4 Plotting after REVIGO

REViGO (http://revigo.irb.hr/) is an online tool that takes a list of GO terms and p values as input and outputs a reduced list of GO categories. It does this by finding GO terms that are similar to each other, and based on shared genes, it assigns “uniqueness” and “dispensability” scores for each GO category. We can filter out GO categoeis that have high dispensability score—these GO categories are redundant because they have been represented by other GO categories in the list.

# write for running revigo
write.table(
  go.pdat[, c("ID", "p.adjust")], 
  file = "../results/04_de-genes-by-celltype/logfc_0.40/go/revigo/GO_for-revigo.txt", 
  quote = FALSE, sep = "\t", row.names = FALSE, col.names = TRUE)
revigo <- read.csv("../results/04_de-genes-by-celltype/logfc_0.40/go/revigo/REVIGO.csv")
# plotting subset
to.keep <- revigo$term_ID[revigo$dispensability < 0.85]
go.sub <- go.pdat[go.pdat$ID %in% to.keep, ]

p <- dotplot_go(go.pdat.sub = go.sub)

ggsave(
  p, 
  filename = "../results/04_de-genes-by-celltype/logfc_0.40/go/plots/go_dotplot_revigo.pdf",
  width = 5.75, height = 6, units = "in"
)

print(p)

8.2.5 Plotting after REViGO: only terminal nodes of DAG

GO terms are hierarchically organized in a directed acyclic graph (DAG). Sometimes you can have two terms enriched where one is more inclusive than the other, and the less inclusive one is the more informative one. For example, if you have “cell killing” and “negative regulation of cell killing” as two enriched GO terms, the latter—the more informative one—is a child term of the the former. The parent-child relationship of GO terms is another axis along which we can prune the list of GO terms, by discarding terms whose child terms are also enriched.

# function for extracting terminal nodes (terms whose child terms are not enriched)
terminal <- function(terms, ontology=c("C", "P", "F"))
{
  FUN <- switch(match.arg(ontology),  
                C = GOCCPARENTS,
                P = GOBPPARENTS, 
                F = GOMFPARENTS)
  terminal <- terms
  seen <- c(terms, "all")
  while (length(terms)) {
    seen <- c(terms, seen)
    terms <- mappedRkeys(FUN[terms])
    terminal <- terminal[!terminal %in% terms]
    terms <- terms[!terms %in% seen]
  }
  terminal
}
# run revigo output terms through terminal function
leaf <- terminal(terms = as.character(revigo$term_ID), ontology = "P")
length(leaf)
[1] 46
# plot
p <- dotplot_go(go.pdat.sub = go.pdat[go.pdat$ID %in% leaf, ])

ggsave(
  p, 
  filename = "../results/04_de-genes-by-celltype/logfc_0.40/go/plots/go_dotplot_revigo_terminal-nodes.pdf",
  width = 6, height = 5.25, units = "in"
)

print(p)

9 Session Info

sessionInfo()
R version 4.0.2 (2020-06-22)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Mojave 10.14.6

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] parallel  stats4    stats     graphics  grDevices utils     datasets 
[8] methods   base     

other attached packages:
 [1] GO.db_3.11.4           org.Hs.eg.db_3.11.4    AnnotationDbi_1.50.3  
 [4] IRanges_2.22.2         S4Vectors_0.26.1       Biobase_2.48.0        
 [7] BiocGenerics_0.34.0    clusterProfiler_3.16.1 ungeviz_0.1.0         
[10] ggtext_0.1.0           cowplot_1.1.0          ggforce_0.3.2         
[13] ggrepel_0.8.2          ggthemes_4.2.0         patchwork_1.0.1       
[16] Seurat_3.2.1           forcats_0.5.0          stringr_1.4.0         
[19] dplyr_1.0.2            purrr_0.3.4            readr_1.3.1           
[22] tidyr_1.1.2            tibble_3.0.3           ggplot2_3.3.2         
[25] tidyverse_1.3.0       

loaded via a namespace (and not attached):
  [1] backports_1.1.10      readxl_1.3.1          fastmatch_1.1-0      
  [4] plyr_1.8.6            igraph_1.2.5          lazyeval_0.2.2       
  [7] splines_4.0.2         BiocParallel_1.22.0   listenv_0.8.0        
 [10] urltools_1.7.3        digest_0.6.25         htmltools_0.5.0      
 [13] GOSemSim_2.14.1       viridis_0.5.1         fansi_0.4.1          
 [16] magrittr_1.5          memoise_1.1.0         tensor_1.5           
 [19] cluster_2.1.0         ROCR_1.0-11           limma_3.44.3         
 [22] globals_0.12.5        graphlayouts_0.7.0    modelr_0.1.8         
 [25] matrixStats_0.56.0    enrichplot_1.8.1      prettyunits_1.1.1    
 [28] colorspace_1.4-1      rvest_0.3.6           blob_1.2.1           
 [31] haven_2.3.1           xfun_0.16             crayon_1.3.4         
 [34] jsonlite_1.7.1        scatterpie_0.1.4      spatstat_1.64-1      
 [37] spatstat.data_1.4-3   survival_3.2-3        zoo_1.8-8            
 [40] glue_1.4.2            polyclip_1.10-0       gtable_0.3.0         
 [43] leiden_0.3.3          future.apply_1.6.0    abind_1.4-5          
 [46] scales_1.1.1          DOSE_3.14.0           DBI_1.1.0            
 [49] miniUI_0.1.1.1        Rcpp_1.0.5            gridtext_0.1.1       
 [52] viridisLite_0.3.0     xtable_1.8-4          progress_1.2.2       
 [55] gridGraphics_0.5-0    reticulate_1.16       bit_4.0.4            
 [58] europepmc_0.4         rsvd_1.0.3            htmlwidgets_1.5.1    
 [61] httr_1.4.2            fgsea_1.14.0          RColorBrewer_1.1-2   
 [64] ellipsis_0.3.1        ica_1.0-2             pkgconfig_2.0.3      
 [67] farver_2.0.3          dbplyr_1.4.4          uwot_0.1.8           
 [70] deldir_0.1-28         labeling_0.3          ggplotify_0.0.5      
 [73] tidyselect_1.1.0      rlang_0.4.7           reshape2_1.4.4       
 [76] later_1.1.0.1         munsell_0.5.0         cellranger_1.1.0     
 [79] tools_4.0.2           cli_2.0.2             downloader_0.4       
 [82] generics_0.0.2        RSQLite_2.2.0         broom_0.7.0          
 [85] ggridges_0.5.2        fastmap_1.0.1         yaml_2.2.1           
 [88] goftest_1.2-2         fs_1.5.0              knitr_1.30           
 [91] bit64_4.0.4           fitdistrplus_1.1-1    tidygraph_1.2.0      
 [94] RANN_2.6.1            ggraph_2.0.3          pbapply_1.4-3        
 [97] future_1.18.0         nlme_3.1-149          mime_0.9             
[100] DO.db_2.9             xml2_1.3.2            compiler_4.0.2       
[103] rstudioapi_0.11       plotly_4.9.2.1        png_0.1-7            
[106] spatstat.utils_1.17-0 reprex_0.3.0          tweenr_1.0.1         
[109] stringi_1.5.3         strapgod_0.0.4.9000   lattice_0.20-41      
[112] Matrix_1.2-18         vctrs_0.3.4           pillar_1.4.6         
[115] lifecycle_0.2.0       BiocManager_1.30.10   triebeard_0.3.0      
[118] lmtest_0.9-38         RcppAnnoy_0.0.16      data.table_1.13.0    
[121] irlba_2.3.3           httpuv_1.5.4          qvalue_2.20.0        
[124] R6_2.4.1              promises_1.1.1        KernSmooth_2.23-17   
[127] gridExtra_2.3         codetools_0.2-16      assertthat_0.2.1     
[130] MASS_7.3-52           withr_2.3.0           sctransform_0.2.1    
[133] mgcv_1.8-31           hms_0.5.3             grid_4.0.2           
[136] rpart_4.1-15          rvcheck_0.1.8         Rtsne_0.15           
[139] lubridate_1.7.9       shiny_1.5.0          

10 References

Pique-Regi, R., R. Romero, A. L. Tarca, E. D. Sendler, Y. Xu, V. Garcia-Flores, Y. Leng, F. Luca, S. S. Hassan, and N. Gomez-Lopez. 2019. “Single Cell Transcriptional Signatures of the Human Placenta in Term and Preterm Parturition.” eLife 8: e52004.

Rusinova, I., S. Forster, S. Yu, A. Kannan, M. Masse, H. Cumming, R. Chapman, and P. J. Hertzog. 2012. “INTERFEROME v2.0: an updated database of annotated interferon-regulated genes.” Nucleic Acids Research 41 (D1): D1040–D1046. https://doi.org/10.1093/nar/gks1215.

LS0tCnRpdGxlOiAiREUgYmV0d2VlbiBjb3ZpZCBhbmQgY29udHJvbCBieSBjZWxsIHR5cGUiCmF1dGhvcjogIkFydW4gQ2hhdmFuIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCmJpYmxpb2dyYXBoeTogLi4vcmVmcy5iaWIKLS0tClN0YXJ0ZWQ6IDIwMjAtMDktMjUgIApMYXN0IGVkaXRlZDogYHIgZm9ybWF0KFN5cy50aW1lKCkpYAoKYGBge3IgbWVzc2FnZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCgojIHNpbmdsZSBjZWxsCmxpYnJhcnkoU2V1cmF0KQoKIyBwbG90dGluZwpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGdnZm9yY2UpICMgZm9yIHNpbmEgcGxvdHMKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KGdndGV4dCkKbGlicmFyeSh1bmdldml6KSAjIGZvciBnZW9tX2hwbGluZQoKIyBnbyBlbnJpY2htZW50CmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShHTy5kYikKYGBgCgojIEJhY2tncm91bmQKT3VyIGdvYWwgaGVyZSBpcyB0byBwZXJmb3JtIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIGJldHdlZW4gY292aWQgYW5kIGNvbnRyb2wgc2FtcGxlcyBzZXBhcmF0ZWx5IGZvciBlYWNoIGFubm90YXRlZCBjZWxsIHR5cGUgaW4gdGhlIHNpbmdsZSBjZWxsIFJOQS1zZXEgZGF0YS4gCgpUaGlzIGFuYWx5c2lzIGlzIHRoZSBzYW1lIGFzIGAwNV9kZS1ieS1jZWxsdHlwZV9sb2dmYzAuMjUuUm1kYCwgZXhjZXB0IGZvciB0aGUgbG9nIGZvbGQgY2hhbmdlIGN1dG9mZiBmb3IgREUgYW5hbHlzaXMuCgojIERhdGEKV2Ugd2lsbCB1c2UgdGhlIGBTZXVyYXRgIG9iamVjdCBpbiB3aGljaCB3ZSBoYXZlIHNhdmVkIHRoZSBjZWxsIGFubm90YXRpb25zLgoKYGBge3J9CnNldXIgPC0gcmVhZFJEUygiLi4vcmVzdWx0cy8wMl9hbm5vdGF0aW9uL3NldXJhdC1vYmplY3RfYW5ub3RhdGVkLnJkcyIpCmBgYAoKIyBEaWZmZXJlbnRpYWx5IGV4cHJlc3NlZCBnZW5lcwoKV2Ugd2lsbCB1c2UgdGhlICJtZXJnZWQiIGFubm90YXRpb25zIGFzIG91ciBjZWxsdHlwZXMgZm9yIHdoaWNoIHRvIHBlcmZvcm0gREUgYW5hbHlzaXMuIEZvciBpZGVudGlmeWluZyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMsIHdlIHdpbGwgdXNlIGBTZXVyYXQ6OkZpbmRNYXJrZXJzKClgLiBGb3IgdGhpcyB3ZSBmaXJzdCBoYXZlIHRvIGNyZWF0ZSBhIG5ldyBtZXRhZGF0YSBjb2x1bW4gd2l0aCBgY2VsbHR5cGVfY292aWRgIGFuZCBgY2VsbHR5cGVfY29udHJvbGAgdmFsdWVzLCBhbmQgdGhlbiB3ZSBydW4gREUgYmV0d2VlbiB0aGVzZSB0d28gYGlkZW50c2AuIFRoaXMgZnVuY3Rpb24gYnkgZGFmYXVsdCB1c2VzIHRoZSBXaWxjb3hvbiBSYW5rIFN1bSB0ZXN0LCB3aGljaCB3ZSB3b24ndCBjaGFuZ2UuIEJ5IGRlZmF1bHQgdGhpcyBmdW5jdGlvbiBvdXRwdXRzIGFsbCBnZW5lcyBmb3Igd2hpY2ggYGFicyhsb2dGQylgIGlzIGF0IGxlYXN0IGAwLjI1YCwgd2l0aG91dCBmaWx0ZXJpbmcgYnkgcCB2YWx1ZS4gV2Ugd2lsbCBub3QgdXNlIHRoZXNlIGRlZmF1bHRzLiBJbnN0ZWFkOgoKMS4gRm9sZCBjaGFuZ2UgY3V0b2ZmOiAkfGxvZyhGb2xkIENoYW5nZSl8ID4gMC40JCAgCjIuICRwX3thZGp9IDwgMC4wNSQKCmBTZXVyYXQ6OkZpbmRNYXJrZXJzKClgIHVzZXMgbmF0dXJhbCBsb2csIG5vdCBsb2cyLCBhbmQgdXNlcyBib25mZXJyb25pIGFkanVzdG1lbnQgdG8gY29ycmVjdCBmb3IgbXVsdGlwbGUgdGVzdGluZyAoc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9zYXRpamFsYWIvc2V1cmF0L2lzc3Vlcy83NDEpLiAkfGxvZyhGb2xkQ2hhbmdlKXwgPiAwLjQkIGNvcnJlc3BvbmRzIHRvIGBleHAoMC40KSA9YCBgciByb3VuZChleHAoMC40KSwgMilgIG9yIGF0IGxlYXN0IDUwJSBjaGFuZ2UgaW4gZXhwcmVzc2lvbi4gICAgCgpUaGUgb3V0cHV0IG9mIHRoaXMgZm9yIGVhY2ggY2VsbCB0eXBlIGlzIHN0b3JlZCBhcyBgLmNzdmAgaW4gYHJlc3VsdHMvMDRfZGUtZ2VuZXMtYnktY2VsbHR5cGUvbG9nZmNfMC40MC9kZS9maWxlc2AuIAoKYGBge3J9CiMgc2V0IHVwCnNldXIkY2VsbHR5cGVfY292aWQgPC0gcGFzdGUoc2V1ciRhbm5vdGF0aW9uX21lcmdlZCwgc2V1ciRjb3ZpZCwgc2VwID0gIl8iKQpJZGVudHMoc2V1cikgPC0gc2V1ciRjZWxsdHlwZV9jb3ZpZApgYGAKCmBgYHtyfQojIGRlCmRlLmdlbmVzIDwtIGxpc3QoKQoKZm9yIChpIGluIHVuaXF1ZShzZXVyJGFubm90YXRpb25fbWVyZ2VkKSkgewogIGRlLmdlbmVzW1tpXV0gPC0gIEZpbmRNYXJrZXJzKHNldXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkZW50LjEgPSBwYXN0ZTAoaSwgIl9jb3ZpZCIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZGVudC4yID0gcGFzdGUwKGksICJfY250cmwiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJTQ1QiLCB2ZXJib3NlID0gRkFMU0UpICU+JSAKICAgIGRwbHlyOjpmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSAmIGFicyhhdmdfbG9nRkMpID4gMC40KSAlPiUgCiAgICBkcGx5cjo6cmVuYW1lKHBjdC5jb3ZpZCA9IHBjdC4xLAogICAgICAgICAgICAgICAgICBwY3QuY250cmwgPSBwY3QuMikKfQpgYGAKCmBgYHtyfQojIHdyaXRlIERFIGdlbmVzIHRvIGZpbGVzCmZvcihpIGluIHVuaXF1ZShzZXVyJGFubm90YXRpb25fbWVyZ2VkKSkgewogIHdyaXRlLmNzdigKICAgIGRlLmdlbmVzW1tpXV0sCiAgICBmaWxlID0gcGFzdGUwKCIuLi9yZXN1bHRzLzA0X2RlLWdlbmVzLWJ5LWNlbGx0eXBlL2xvZ2ZjXzAuNDAvZGUvZmlsZXMvZGUtZ2VuZXNfIiwgaSwgIi5jc3YiICksCiAgICByb3cubmFtZXMgPSBUUlVFKQp9CmBgYAoKIyMgTnVtYmVyIG9mIERFIGdlbmVzIGJ5IGNlbGx0eXBlCmBgYHtyIGZpZy53aWR0aD0yLCBmaWcuYXNwID0gMC43NX0KZGUubnVtYmVyIDwtIGxhcHBseShkZS5nZW5lcywgbnJvdykgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgdCgpICU+JSBhcy5kYXRhLmZyYW1lKCkKY29sbmFtZXMoZGUubnVtYmVyKSA8LSAibnVtYmVyIgpkZS5udW1iZXIkY2VsbHR5cGUgPC0gcm93bmFtZXMoZGUubnVtYmVyKQoKZGUubnVtYmVyIDwtIGRlLm51bWJlcltvcmRlcihkZS5udW1iZXIkbnVtYmVyLCBkZWNyZWFzaW5nID0gVFJVRSksIF0KZGUubnVtYmVyJGNlbGx0eXBlIDwtIGZhY3RvcihkZS5udW1iZXIkY2VsbHR5cGUsIGxldmVscyA9IGRlLm51bWJlciRjZWxsdHlwZSkKCnAgPC0gZ2dwbG90KGRhdGEgPSBkZS5udW1iZXIsIGFlcyh4ID0gY2VsbHR5cGUsIHkgPSBudW1iZXIsIGxhYmVsID0gbnVtYmVyKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImdyZXk1MCIsIHdpZHRoID0gMC44KSArCiAgZ2VvbV90ZXh0KHNpemUgPSAxLjgsIHZqdXN0ID0gLTAuMjUpICsKICBnZ3RpdGxlKCJOdW1iZXIgb2YgREUgZ2VuZXMiKSArCiAgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjIpLAogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC43NSwgImxpbmVzIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDYpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgID0gNiksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNykpCgpnZ3NhdmUocCwgZmlsZW5hbWUgPSAiLi4vcmVzdWx0cy8wNF9kZS1nZW5lcy1ieS1jZWxsdHlwZS9sb2dmY18wLjQwL2RlL3Bsb3RzL251bWJlci1vZi1kZS1nZW5lcy5wZGYiLCB3aWR0aCA9IDMuNSwgaGVpZ2h0ID0gMywgdW5pdHMgPSAiaW4iKQoKcHJpbnQocCkKYGBgCgojIyBNaXRvY2hvbmRyaWFsIFVNSQpGb3Igc29tZSBjZWxsIHR5cGVzIGEgbG90IG9mIG1pdG9jaG9uZHJpYWwgZ2VuZXMgYXJlIERFIGJldHdlZW4gY292aWQgYW5kIGNvbnRyb2wuIEl0J3MgcG9zc2libGUgdGhhdCB0aG9zZSBnZW5lcyBhcmUgcmVhbGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCwgYnV0IGl0J3MgbW9yZSBsaWtlbHkgdGhhdCB0aGVyZSBpcyBhIGRpZmZlcmVuY2UgYmV0d2VlbiBjb3ZpZCBhbmQgY29udHJvbCBzYW1wbGVzIGluIHRlcm1zIG9mIHF1YWxpdHkgb2YgY2VsbC1wcmVwLiBXZSBjYW4gbG9vayBhdCBwZXJjZW50IG1pdG9jaG9uZHJpYWwgVU1JIGluIGFsbCBjZWxsIHR5cGVzIGJ5IGNvdmlkIHN0YXR1cyB0byBzZWUgaWYgdGhpcyBpcyB0cnVlLgoKYGBge3IgZmlnLmFzcD0wLjU1LCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBzZXVyQG1ldGEuZGF0YSwgYWVzKHggPSBjb3ZpZCwgeSA9IHBlcmNlbnQubXQpKSArCiAgZ2VvbV9zaW5hKGFlcyhjb2xvciA9IGNvdmlkKSwgc2l6ZSA9IDEsIGFscGhhID0gMSkgKwogIHNjYWxlX2NvbG9yX3RhYmxlYXUoKSArCiAgZmFjZXRfZ3JpZCgufiBhbm5vdGF0aW9uX21lcmdlZCkgKwogIGxhYnMoY2FwdGlvbiA9ICIlIG1pdG9jaG9uZHJpYWwgcmVhZHMgaW4gYWxsIGNlbGwgdHlwZXMiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShzaXplID0gMC4yKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDAsIHZqdXN0ID0gMC41KSkgCmBgYAoKRm9yIG1vc3QgY2VsbCB0eXBlcyB0aGVyZSBhcHBlYXJzIHRvIGJlIGEgZGlmZmVyZW5jZSBpbiB0aGUgZGlzdHJpYnV0aW9uIG9mIHBlcmNlbnQgbWl0b2Nob25kcmlhbCByZWFkcyBiZXR3ZWVuIGNvdmlkIGFuZCBjb250cm9sIHNhbXBsZXMuIENvdmlkIHNhbXBsZXMgZ2VuZXJhbGx5IGhhdmUgbG93ZXIgJSBtaXRvY2hvbmRyaWFsIHJlYWRzLiBOZXh0LCB3ZSBjYW4gcGxvdCB0aGUgYWJzb2x1dGUgdmFsdWUgb2YgdGhlIGRpZmZlcmVuY2UgaW4gcGVyY2VudCBtaXRvY2hvbmRyaWFsIHJlYWRzIGJldHdlZW4gY292aWQgYW5kIGNvbnRyb2wgYWdhaW5zdCB0aGUgbnVtYmVyIG9mIERFIGdlbmVzIHRoYXQgYXJlIG1pdG9jaG9uZHJpYWwuIElmIHRoZSBtaXRvY2hvbmRyaWFsIGdlbmVzIGluIERFIGFuYWx5c2lzIGFyZSBkcml2ZW4gYnkgZGlmZmVyZW5jZXMgaW4gc2FtcGxlIHF1YWxpdHksIHdlIHNob3VsZCBleHBlY3QgYSBwb3NpdGl2ZSByZWxhdGlvbnNoaXAuIAoKYGBge3J9Cm10IDwtIGRhdGEuZnJhbWUoCiAgY2VsbHR5cGUgPSBzb3J0KG5hbWVzKGRlLmdlbmVzKSksCiAgbXQuZGlmZiA9IE5BLAogIG10LmRlID0gTkEKKQoKZm9yKGkgaW4gbXQkY2VsbHR5cGUpIHsKICBtdCRtdC5kaWZmW210JGNlbGx0eXBlID09IGldIDwtIGFicygKICAgIG1lZGlhbihzZXVyJHBlcmNlbnQubXRbc2V1ciRhbm5vdGF0aW9uX21lcmdlZCA9PSBpICYgc2V1ciRjb3ZpZCA9PSAiY292aWQiXSkgLQogICAgICBtZWRpYW4oc2V1ciRwZXJjZW50Lm10W3NldXIkYW5ub3RhdGlvbl9tZXJnZWQgPT0gaSAmIHNldXIkY292aWQgPT0gImNudHJsIl0pCiAgKQogIAogIG10JG10LmRlW210JGNlbGx0eXBlID09IGldIDwtIGdyZXAoIk1ULSIsIHJvd25hbWVzKGRlLmdlbmVzW1tpXV0pKSAlPiUgbGVuZ3RoKCkKfQpgYGAKCmBgYHtyIGZpZy5hc3AgPSAwLjgsIGZpZy53aWR0aD0zfQpnZ3Bsb3QoZGF0YSA9IG10LCBhZXMoeCA9IG10LmRpZmYsIHkgPSBtdC5kZSwgbGFiZWwgPSBjZWxsdHlwZSkpICsKICBnZW9tX3BvaW50KHNoYXBlID0gMjEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJncmV5NDAiLCBzaXplID0gMykgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCBOQSksIHlsaW0gPSBjKDAsIE5BKSkgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMywgY29sb3IgPSAiZ3JleTQwIikgKwogIGxhYnMoeCA9ICJcbmFicyhtZWRpYW4oJSBtdCBpbiBjb3ZpZCkgLSBtZWRpYW4oJSBtdCBpbiBjb250cm9sKSkiLAogICAgICAgeSA9ICJudW1iZXIgb2YgREUgbWl0b2Nob25kcmlhbCBnZW5lcyIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuMjUpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpCmBgYApUaGVyZSBpcyBhIHZlcnkgbWlsZCBwb3NpdGl2ZSByZWxhdGlvbnNoaXAsIG1vcmUgc28gZm9yIHZpbGxvdXMgY2VsbCB0eXBlcy4gVGhyZWUgZGVjaWR1YWwgY2VsbCB0eXBlcy0tLWRlYy5Nb25vXzEsIGRlYy5Nb25vXzIsIGFuZCBkZWMuVGNlbGxfMi0tLWhhdmUgaGlnaCBudW1iZXIgb2YgTVQgREUgZ2VuZXMgZGVzcGl0ZSBsb3cgZGlmZmVyZW5jZSBpbiBwZXJjZW50IE1UIHJlYWRzLiAKCldlIGNhbiBpZ25vcmUgdGhlIG1pdG9jaG9uZHJpYWwgZ2VuZXMgc2luY2UgdGhlIG51bWJlciBvZiBnZW5lcyBpcyBxdWl0ZSBzbWFsbCBjb21wYXJlZCB0byB0aGUgdG90YWwgbnVtYmVyIG9mIERFIGdlbmVzLgoKIyBJbnRlcmZlcm9tZQpJbnRlcmZlcm9tZSBbQHJ1c2lub3ZhX2ludGVyZmVyb21lXzIwMTJdICBpcyBhIG1hbnVhbGx5IGN1cmF0ZWQgZGF0YWJhc2Ugb2YgaW50ZXJmZXJvbi1yZXNwb25zaXZlIGdlbmVzLCBhdmFpbGFibGUgYXQgaHR0cDovL3d3dy5pbnRlcmZlcm9tZS5vcmcvaW50ZXJmZXJvbWUvaG9tZS5qc3B4LiBUaGUgZGF0YWJhc2UgZXNzZW50aWFsbHkgY29udGFpbnMgYSBsaXN0IG9mIGFsbCBnZW5lcyB3aG9zZSBleHByZXNzaW9uIGlzIGFmZmVjdGVkIGJ5IGludGVyZmVyb25zLCBhcyBpbmZlcnJlZCBmcm9tIHZhcmlvdXMgZGF0YXNldHMuIFRoZXNlIGRhdGFzZXRzIGluY2x1ZGUgaW4gdml0cm8gYW5kIGluIHZpdm8gc3R1ZGllcyBmcm9tIGh1bWFuIGFuZCBtb3VzZS4gVGhlIG9ubGluZSBpbnRlcmZhY2UgaW5jbHVkZXMgYSBzZWFyY2ggZnVuY3Rpb24gd2hpY2ggYWxsb3dzIHVzZXJzIHRvIGlucHV0IGEgbGlzdCBvZiBnZW5lcywgd2hpY2ggYXJlIHNlYXJjaGVkIGFnYWluc3QgdGhlIGRhdGFiYXNlLiBUaGUgb3V0cHV0IGluY2x1ZGVzIHZhcmlvdXMgdGhpbmdzIGluY2x1ZGluZzogKDEpIGxpc3Qgb2YgZ2VuZXMgZnJvbSB0aGUgaW5wdXQgdGhhdCBhcmUgZm91bmQgaW4gdGhlIGluZmVyZmVyb21lIGRhdGFiYXNlLCBpLmUuIHRoZXJlIGlzIGV2aWRlbmNlIGZvciB0aGVzZSBnZW5lcyB0byBiZSBtb2R1bGF0ZWQgYnkgaW50ZXJmZXJvbiwgKDIpIGEgZGV0YWlsZWQgdGFibGUgd2l0aCBsaW5rcyBwb2ludGluZyB0byB0aGUgZGF0YXNldCBmcm9tIHdoaWNoIHRoZSBldmlkZW5jZSB3YXMgZ2F0aGVyZWQgZm9yIGVhY2ggZ2VuZSwgKDMpIGdlbmUgb250b2xvZ3kgZW5yaWNobWVudCwgKDQpIFRGIGJpbmRpbmcgc2l0ZSBhbmFseXNpcywgZXRjLiAKCkkgcmFuIHRoZSBsaXN0IG9mIERFIGdlbmVzIGZvciBlYWNoIG9mIHRoZSAyMSBjZWxsdHlwZXMgdGhyb3VnaCBJbnRlcmZlcm9tZSAoYHZlcnNpb24gMi4wMWAsIDIwMjAtMDktMjApIGFuZCBzYXZlZCB0aGUgdGV4dCBvdXRwdXQgZm9yICgxKSBmcm9tIGFib3ZlLiBJIHVzZWQgdGhlIGZvbGxvd2luZyBzZWFyY2ggcGFyYW1ldGVycyAoYWxsIGRlZmF1bHRzIGV4Y2VwdCBmb3IgbmFycm93aW5nIHNwZWNpZXMgdG8gKkhvbW8gc2FwaWVucyopCgpgYGAKSW50ZXJmZXJvbWUgVHlwZToJQW55CkludGVyZmVyb21lIFN1YlR5cGU6CUFueQpUcmVhdG1lbnQgQ29uY2VudHJhdGlvbjoJQW55ClRyZWF0bWVudCBUaW1lOglBbnkKVml2by9WaXRybzoJQW55ClNwZWNpZXM6CUhvbW8gc2FwaWVucwpTeXN0ZW06CUFueQpPcmdhbjoJQW55CkNlbGw6CUFueQpDZWxsIExpbmU6CUFueQpOb3JtYWwvQWJub3JtYWw6CUFueQpGb2xkIENoYW5nZSBVcDoJMi4wCkZvbGQgQ2hhbmdlIERvd246CTIuMApgYGAKCiMjIEZyYWN0aW9uIG9mIERFIGluIGludGVyZmVyb21lIGRhdGFiYXNlCgpXZSB3YW50IHRvIGNhbGN1bGF0ZSBmb3IgZWFjaCBjZWxsdHlwZSwgdGhlIGZyYWN0aW9uIG9mIERFIGdlbmVzIChiZXR3ZWVuIGNvdmlkIGFuZCBjb250cm9sKSB3aGljaCBhcmUga25vd24gdG8gYmUgaW50ZXJmZXJvbi1yZXNwb25zaXZlLiBGb3IgdGhpcywgYmVsb3cgd2Ugd2lsbCByZWFkIHRoZSBJbnRlcmZlcm9tZSBvdXRwdXQgdGV4dCBmaWxlcyBhbmQgY2FsY3VsYXRlIHRoYXQgZnJhY3Rpb24uIAoKYGBge3J9CiMgcmVhZCBpbnRlcmZlcm9tZSBvdXRwdXQKaWZvbWUgPC0gbGlzdCgpCgpmb3IoaSBpbiBuYW1lcyhkZS5nZW5lcykpIHsKICBpZm9tZVtbaV1dIDwtIHJlYWQudGFibGUoCiAgICBwYXN0ZTAoCiAgICAgICIuLi9yZXN1bHRzLzA0X2RlLWdlbmVzLWJ5LWNlbGx0eXBlL2xvZ2ZjXzAuNDAvaW50ZXJmZXJvbWUvZmlsZXMvaW50ZXJmZXJvbWVfIiwgCiAgICAgIGksICIudHh0IiksIAogICAgaGVhZGVyID0gVFJVRSwgc2VwID0gIlx0IiwgcXVvdGUgPSAiIiwgc2tpcCA9IDE5LCAKICAgIHN0cmlwLndoaXRlID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQp9CmBgYAoKYGBge3J9CiMgY3JlYXRlIGEgZGF0YWZyYW1lIG9mIG51bWJlciBvZiBnZW5lcyBpbiBpbnRlcmZlcm9tZSBmb3IgZWFjaCBjZWxsIHR5cGUKaWZvbWUubiA8LSBkYXRhLmZyYW1lKAogIGNlbGx0eXBlID0gbmFtZXMoZGUuZ2VuZXMpLAogIG5fZGUgPSBOQSwKICBuX2ludGVyZmVyb21lLnllcyA9IE5BCikKCiMgY2FsY3VsYXRlIG51bWJlciBvZiBnZW5lcwpmb3IgKGkgaW4gbmFtZXMoZGUuZ2VuZXMpKSB7CiAgCiAgaWZvbWUubiRuX2RlW2lmb21lLm4kY2VsbHR5cGUgPT0gaV0gPC0gbnJvdyhkZS5nZW5lc1tbaV1dKQogIAogIGlmb21lLm4kbl9pbnRlcmZlcm9tZS55ZXNbaWZvbWUubiRjZWxsdHlwZSA9PSBpXSA8LSAKICAgIChyb3duYW1lcyhkZS5nZW5lc1tbaV1dKSAlaW4lIGlmb21lW1tpXV0kR2VuZS5OYW1lKSAlPiUgCiAgICBzdW0oKQoKfQoKaWZvbWUubiRuX2ludGVyZmVyb21lLm5vIDwtIGlmb21lLm4kbl9kZSAtIGlmb21lLm4kbl9pbnRlcmZlcm9tZS55ZXMKCiMgY2FsY3VsYXRlIHBlcmNlbnRhZ2VzCmlmb21lLm4kcGN0X2ludGVyZmVyb21lLnllcyA8LSAoaWZvbWUubiRuX2ludGVyZmVyb21lLnllcy9pZm9tZS5uJG5fZGUpICogMTAwCmlmb21lLm4kcGN0X2ludGVyZmVyb21lLm5vIDwtIDEwMCAtIGlmb21lLm4kcGN0X2ludGVyZmVyb21lLnllcwpgYGAKCmBgYHtyfQojIHByZXBhcmUgZm9yIHBsb3R0aW5nCiMgbWVsdAppZm9tZS5uLmxvbmcgPC0gcGl2b3RfbG9uZ2VyKGRhdGEgPSBpZm9tZS5uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHMgPSAzOjYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSBjKCIudmFsdWUiLCAiaW50ZXJmZXJvbWUiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfcGF0dGVybiA9ICIoLispXyguKykiKQppZm9tZS5uLmxvbmckaW50ZXJmZXJvbWUgPC0gZ3N1YigiaW50ZXJmZXJvbWUuIiwgIiIsIGlmb21lLm4ubG9uZyRpbnRlcmZlcm9tZSkKCmlmb21lLm4ubG9uZyRjZWxsdHlwZSA8LSBmYWN0b3IoaWZvbWUubi5sb25nJGNlbGx0eXBlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBkZS5udW1iZXIkY2VsbHR5cGUpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjUsIGZpZy53aWR0aD0zfQojIHBsb3QgaXRlcmZlcm9tZSBvdXRwdXQKCnRoZW1lMiA8LSAgIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoCiAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNSwgImxpbmVzIiksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDUuNSksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwLjUsIHNpemUgPSA2LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiwgY29sb3IgPSAiYmxhY2siKSwKICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjI1KSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfbGluZShzaXplID0gMC4yNSksCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuMjUpKQoKcC5uIDwtIGdncGxvdChkYXRhID0gaWZvbWUubi5sb25nLCAKICAgICAgICAgICAgICBhZXMoeSA9IGNlbGx0eXBlLCB4ID0gbikpICsKICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQgPSAiaWRlbnRpdHkiLCAKICAgICAgICAgICBhZXMoZmlsbCA9IGludGVyZmVyb21lKSwgd2lkdGggPSAwLjgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJncmV5OTAiLCAiIzRlNzlhNyIpLCAKICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCJubyIsICJ5ZXMiKSkgKwogIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kID0gYygwLjA1LCAwKSkgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAuMDI1LCAwKSkgKwogIGdlb21fdGV4dChkYXRhID0gaWZvbWUubi5sb25nW2lmb21lLm4ubG9uZyRpbnRlcmZlcm9tZSA9PSAieWVzIiwgXSwgCiAgICAgICAgICAgIGFlcyh5ID0gY2VsbHR5cGUsIHggPSBuLCBsYWJlbCA9IG4pLAogICAgICAgICAgICBzaXplID0gMS44LCB2anVzdCA9IDAuNSwgaGp1c3QgPSAwLjAsIG51ZGdlX3ggPSAxKSArCiAgbGFicyh4ID0gIm51bWJlciIpICsKICB0aGVtZTIgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC45MCwgMC45MCkpCgpwLnBjdCA8LSBnZ3Bsb3QoZGF0YSA9IGlmb21lLm4ubG9uZywgCiAgICAgICAgICAgICAgICBhZXMoeSA9IGNlbGx0eXBlLCB4ID0gcGN0KSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdCA9ICJpZGVudGl0eSIsIAogICAgICAgICAgIGFlcyhmaWxsID0gaW50ZXJmZXJvbWUpLCB3aWR0aCA9IDAuOCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdyZXk5MCIsICIjNGU3OWE3IiksIAogICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoIm5vIiwgInllcyIpKSArCiAgc2NhbGVfeV9kaXNjcmV0ZShleHBhbmQgPSBjKDAuMDUsIDApKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMC4wMjUsIDApKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBpZm9tZS5uLmxvbmdbaWZvbWUubi5sb25nJGludGVyZmVyb21lID09ICJ5ZXMiLCBdLCAKICAgICAgICAgICAgYWVzKHkgPSBjZWxsdHlwZSwgeCA9IHBjdCwgbGFiZWwgPSByb3VuZChwY3QsIDApKSwKICAgICAgICAgICAgc2l6ZSA9IDEuOCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMC4wLCBudWRnZV94ID0gMC41KSArCiAgbGFicyh4ID0gInBlcmNlbnQiKSArCiAgdGhlbWUyICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgoKcGxvdF9yb3cgPC0gcGxvdF9ncmlkKHAubiwgcC5wY3QpCgojIG5vdyBhZGQgdGhlIHRpdGxlCnRpdGxlIDwtIGdnZHJhdygpICsKICBkcmF3X2xhYmVsKCJERSBnZW5lcyBpbiBpbnRlcmZlcm9tZSBkYXRhYmFzZSIsIHggPSAwLCBoanVzdCA9IDAsIHNpemUgPSA4KSArCiAgdGhlbWUoCiAgICAjIGFkZCBtYXJnaW4gb24gdGhlIGxlZnQgb2YgdGhlIGRyYXdpbmcgY2FudmFzLAogICAgIyBzbyB0aXRsZSBpcyBhbGlnbmVkIHdpdGggbGVmdCBlZGdlIG9mIGZpcnN0IHBsb3QKICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDAsIDAsIDAsIDcpCiAgKQoKcCA8LSBwbG90X2dyaWQodGl0bGUsIHBsb3Rfcm93LAogICAgICAgICAgICAgICBuY29sID0gMSwKICAgICAgICAgICAgICAgIyByZWxfaGVpZ2h0cyB2YWx1ZXMgY29udHJvbCB2ZXJ0aWNhbCB0aXRsZSBtYXJnaW5zCiAgICAgICAgICAgICAgIHJlbF9oZWlnaHRzID0gYygwLjA1LCAxKQopCgpnZ3NhdmUocCwKICAgICAgIGZpbGVuYW1lID0gIi4uL3Jlc3VsdHMvMDRfZGUtZ2VuZXMtYnktY2VsbHR5cGUvbG9nZmNfMC40MC9pbnRlcmZlcm9tZS9wbG90cy9kZS1nZW5lcy1pbnRlcmZlcm9tZS5wZGYiLAogICAgICAgd2lkdGggPSA2LCBoZWlnaHQgPSAzLCB1bml0cyA9ICJpbiIpCgpwcmludChwKQpgYGAKCiMjIFRlc3QgZm9yIGVucmljaG1lbnQKCkZvciBtb3N0IGNlbGwgdHlwZXMsIGEgbGFyZ2UgY2h1bmsgb2YgZ2VuZXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gY292aWQgYW5kIGNvbnRyb2wgYXJlIGluIGZhY3Qga25vd24gdG8gYmUgcmVzcG9uc2l2ZSB0byBpbnRlcmZlcm9ucyBhY2NvcmRpbmcgdG8gSW50ZXJmZXJvbWUuIFRvIGZpbmQgb3V0IHdoZXRoZXIgdGhpcyBmcmFjdGlvbiBpcyBsYXJnZXIgdGhhbiB3aGF0IG9uZSB3b3VsZCBleHBlY3QgYnkgY2hhbmNlLCB3ZSBjYW4gZG8gYSBoeXBlcmdlb21ldHJpYyB0ZXN0IGZvciBlbnJpY2htZW50LCBzaW1pbGFyIHRvIHRoZSBvbmUgdGhhdCB3ZSBkbyBmb3IgR08gZW5yaWNobWVudC4gCgpJZiAkTiQgaXMgdGhlIHRvdGFsIG51bWJlciBvZiBnZW5lcyB1c2VkIGluIERFIGFuYWx5c2lzLCAkQiQgaXMgdGhlIG51bWJlciBvZiBnZW5lcyBvdXQgb2YgJE4kIHRoYXQgYXJlIGluIHRoZSBpbnRlcmZlcm9tZSBkYXRhYmFzZSwgJG4kIGlzIHRoZSBudW1iZXIgb2YgREUgZ2VuZXMgZm9yIGEgY2VsbCB0eXBlLCBhbmQgJGIkIGlzIHRoZSBudW1iZXIgb2YgZ2VuZXMgZ2VuZXMgb3V0IG9mICRuJCB0aGF0IGFyZSBpbiB0aGUgaW50ZXJmZXJvbWUgZGF0YWJhc2UsIHRoZW46ICRlbnJpY2htZW50ID0gXGZyYWN7Yn17bn0vXGZyYWN7Qn17Tn0kLiBBbmQgdGhlIGh5cGVyZ2VvbWV0cmljIGRpc3RyaWJ1dGlvbiwgd2UgY2FuIGNhbGN1bGF0ZSB0aGUgKnAqIHZhbHVlICh0aGUgY3VtdWxhdGl2ZSBwcm9iYWJpbGl0eSBvZiBmaW5kaW5nICRiJCBvciBtb3JlIGdlbmVzIGluIGEgc2FtcGxlIG9mICRuJCBERSBnZW5lcywgZ2l2ZW4gJEIkIGFuZCAkTiQpLgoKYGBge3IgfQojIHRvdGFsIG51bWJlciBvZiBnZW5lcyBpbiB0aGUgZGF0YXNldAppZm9tZS5uJE4gPC0gbnJvdyhzZXVyQGFzc2F5cyRTQ1RAZGF0YSkKCiMgdG90YWwgbnVtYmVyIG9mIGdlbmVzIGluIHRoZSBpbnRlcmZlcm9tZSBkYXRhc2V0Cmlmb21lLm4kQiA8LSAxMjYxNAoKIyBlbnJpY2htZW50Cmlmb21lLm4kZW5yaWNobWVudCA8LSAoaWZvbWUubiRuX2ludGVyZmVyb21lLnllcy8gaWZvbWUubiRuX2RlKS8KICAoaWZvbWUubiRCL2lmb21lLm4kTikKCiMgcCB2YWx1ZQojcGh5cGVyKHEgPSAyNDQtMSwgbSA9IDEyNjE0LCBuID0gMjM1MDItMTI2MTQsIGsgPSAzNTUsIGxvd2VyLnRhaWwgPSBGQUxTRSkKCmlmb21lLm4kcHZhbCA8LSBwaHlwZXIoCiAgcSA9IGlmb21lLm4kbl9pbnRlcmZlcm9tZS55ZXMgLSAxLAogIG0gPSBpZm9tZS5uJEIsCiAgbiA9IGlmb21lLm4kTiAtIGlmb21lLm4kQiwKICBrID0gaWZvbWUubiRuX2RlLAogIGxvd2VyLnRhaWwgPSBGQUxTRQopCgojIGNvbG9ycyBmb3IgcGxvdAppZm9tZS5uJGNvbG9yIDwtIGlmZWxzZShpZm9tZS5uJHB2YWwgPCAwLjA1LCB5ZXMgPSAiQmxhY2siLCBubyA9ICJHcmV5IikKCmBgYAoKYGBge3J9CiMgd3JpdGUgdG8gZmlsZQppZm9tZS5uICU+JSAKICBkcGx5cjo6cmVuYW1lKCJuX3RvdGFsLmdlbmVzIiA9ICJOIiwgIm5faW50ZXJmZXJvbWUuZ2VuZXMiID0gIkIiKSAlPiUgCiAgZHBseXI6OnNlbGVjdCgtImNvbG9yIikgJT4lIAogIHdyaXRlLmNzdiguLCAiLi4vcmVzdWx0cy8wNF9kZS1nZW5lcy1ieS1jZWxsdHlwZS9sb2dmY18wLjQwL2ludGVyZmVyb21lL2ZpbGVzL251bWJlci1vZi1ERS1nZW5lcy1pbi1pbnRlcmZlcm9tZS5jc3YiLCAKICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKYGBge3J9Cmlmb21lLm5bLCBjKDEsIDIsIDMsIDcsIDgsIDksIDEwKV0gJT4lIGtuaXRyOjprYWJsZSgpCmBgYAoKYGBge3IsIGZpZy5hc3AgPSAwLjgsIGZpZy53aWR0aCA9IDJ9CiMgUGxvdCBmcmFjdGlvbiBvZiBERSBnZW5lcyBpbiBpbnRlcmZlcm9tZSwgYWxvbmcgd2l0aCBlbnJpY2htZW50IHAgdmFsdWVzLgoKcGxvdC5kYXQgPC0gaWZvbWUubi5sb25nCnBsb3QuZGF0JGNlbGx0eXBlIDwtIGZhY3RvcigKICBwbG90LmRhdCRjZWxsdHlwZSwgCiAgbGV2ZWxzID0gaWZvbWUubiRjZWxsdHlwZVtvcmRlcihpZm9tZS5uJHBjdF9pbnRlcmZlcm9tZS55ZXMpXQogICkKcCA8LSBnZ3Bsb3QoZGF0YSA9IHBsb3QuZGF0LCAKICAgICAgICAgICAgYWVzKHkgPSBjZWxsdHlwZSwgeCA9IHBjdCkpICsKICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIAogICAgICAgICAgIHN0YXQgPSAiaWRlbnRpdHkiLCAKICAgICAgICAgICBhZXMoZmlsbCA9IGludGVyZmVyb21lKSwgCiAgICAgICAgICAgd2lkdGggPSAwLjgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJncmV5OTAiLCAiIzRlNzlhNyIpLCAKICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCJubyIsICJ5ZXMiKSwKICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkludGVyZmVyb21lIiwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTm8iLCAiWWVzIikpICsKICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMC4wNSwgMCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLjAyNSwgMCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oY2xpcCA9ICJvZmYiKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBwbG90LmRhdFtwbG90LmRhdCRpbnRlcmZlcm9tZSA9PSAieWVzIiwgXSwgCiAgICAgICAgICAgIGFlcyh5ID0gY2VsbHR5cGUsIHggPSBwY3QsIAogICAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZTAobiwgIi8iLCBuX2RlKSksCiAgICAgICAgICAgIHNpemUgPSAxLjgsIHZqdXN0ID0gMC41LCBoanVzdCA9IDAuMCwgbnVkZ2VfeCA9IDAuNSkgKwogIGxhYnMoeCA9ICJERSBnZW5lcyBwcmVzZW50IGluIEludGVyZmVyb21lICglKSIpICsKICBnZW9tX3RleHQoZGF0YSA9IGlmb21lLm5bb3JkZXIobGV2ZWxzKHBsb3QuZGF0JGNlbGx0eXBlKSksIF0sIAogICAgICAgICAgICBhZXMoeSA9IGNlbGx0eXBlLCB4ID0gMTAwLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0QyhwdmFsLCBmb3JtYXQgPSAiZSIsIGRpZ2l0cyA9IDApKSwKICAgICAgICAgICAgaGp1c3QgPSAwLCBzaXplID0gMS43NSwgbnVkZ2VfeCA9IDEsCiAgICAgICAgICAgIGNvbG9yID0gaWZvbWUubiRjb2xvcltvcmRlcihsZXZlbHMocGxvdC5kYXQkY2VsbHR5cGUpKV0pICsKICBhbm5vdGF0ZShnZW9tID0gInJpY2h0ZXh0IiwgeCAgPSAxMDEsIHkgPSAyMiwgc2l6ZSA9IDIsCiAgICAgICAgICAgbGFiZWwgPSAiZW5yaWNobWVudCAqcCogdmFsdWUiLCBoanVzdCA9IDAsCiAgICAgICAgICAgZmlsbCA9IE5BLCBsYWJlbC5jb2xvciA9IE5BLCAjIHJlbW92ZSBiYWNrZ3JvdW5kIGFuZCBvdXRsaW5lCiAgICAgICAgICAgbGFiZWwucGFkZGluZyA9IGdyaWQ6OnVuaXQoMCwgInB0IikpICsgIyByZW1vdmUgcGFkZGluZwogIHRoZW1lMiArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwKICAgICAgICBsZWdlbmQubWFyZ2luID0gbWFyZ2luKDAsIDAsIDAsIDMpKQoKZ2dzYXZlKHAsIGZpbGVuYW1lID0gIi4uL3Jlc3VsdHMvMDRfZGUtZ2VuZXMtYnktY2VsbHR5cGUvbG9nZmNfMC40MC9pbnRlcmZlcm9tZS9wbG90cy9pbnRlcmZlcm9tZV9wZXJjZW50X2VucmljaG1lbnQtcHZhbC5wZGYiLCB3aWR0aCA9IDMuNSwgaGVpZ2h0ID0gMywgdW5pdHMgPSAiaW4iKQoKcHJpbnQocCkKYGBgCkV2ZW4gdGhvdWdoIHRoZSBlbnJpY2htZW50IGlzIG1vZGVzdCwgYWJvdXQgMS41IGZvbGQsIGZvciBtb3N0IGNlbGwgdHlwZXMsIGl0IGlzIGhpZ2hseSB1bmxpa2VseSBieSBjaGFuY2UuIFRoZSAqcCogdmFsdWVzIGZvciBtb3N0IGNlbGwgdHlwZXMgYXJlIGFsbW9zdCB6ZXJvLiBPbmx5IHRocmVlIGNlbGwgdHlwZXMgaGF2ZSAqcCogdmFsdWVzIGhpZ2hlciB0aGFuIDAuMDUuIAoKIyBHTyBlbnJpY2htZW50CgpJIGFsc28gcmFuIEdPIGVucmljaG1lbnQgKG9ubHkgZm9yIHRoZSBCaW9sb2dpY2FsIFByb2Nlc3MgY2F0ZWdvcnkpIGZvciBERSBnZW5lcyAob25seSB0aG9zZSB0aGF0IGFyZSB1cC1yZWd1bGF0ZWQgaW4gY292aWQpIGluIGFsbCBjZWxsdHlwZXMuIFRoZSB0YWJsZSBvdXRwdXRzIGZvciBhbGwgY2VsbHR5cGVzIGFyZSBzYXZlZCBhcyBgLmNzdmAgaW4gYHJlc3VsdHMvMDRfZGUtZ2VuZXMtYnktY2VsbHR5cGUvbG9nZmNfMC40MC9nby9maWxlc2AuIApGb3IgbW9zdCBjZWxsIHR5cGVzLCB0aGUgZW5yaWNoZWQgR08gY2F0ZWdwcm9lcyBpbmNsdWRlIHRob3NlIHJlbGF0ZWQgdG8gZGVmZW5zZSB0byB2aXJhbCBpbmZlY3Rpb24sIGludGVyZmVyb24gcmVzcG9uc2UgZ2VuZXMsIG1pc2ZvbGRlZCBwcm90ZWluIHJlc3BvbnNlLCBldGMuIFNlZSBiZWxvdyBmb3IgdGhlIHRvcCAxMCBlbnJpY2hlZCBHTyBjYXRlZ29yaWVzIGZvciBERSBnZW5lcyBpbiBlYWNoIGNlbGx0eXBlLgoKYGBge3J9CiMgZnVuY3Rpb24gZm9yIEdPIGVucmljaG1lbnQgb24gdXByZWd1bGF0ZWQgZ2VuZXMgZm9yIGVhY2ggY2VsbCB0eXBlCmVucmljaEdPMiA8LSBmdW5jdGlvbihjZWxsdHlwZSwgLi4uKSB7CiAgCiAgdXAgPC0gZGUuZ2VuZXNbW2NlbGx0eXBlXV0gJT4lIAogICAgZHBseXI6OmZpbHRlcihhdmdfbG9nRkMgPiAwKQogIAogIGlkcy5iaXRyIDwtIGJpdHIocm93bmFtZXModXApLCAKICAgICAgICAgICAgICAgICAgIGZyb21UeXBlID0gIlNZTUJPTCIsIHRvVHlwZSA9IGMoIkVOVFJFWklEIiwgIlNZTUJPTCIpLCAKICAgICAgICAgICAgICAgICAgIE9yZ0RiID0gIm9yZy5Icy5lZy5kYiIpCiAgICAKICBmZWF0dXJlcyA8LSBpZHMuYml0ciRFTlRSRVpJRAoKICBlZ28gPC0gZW5yaWNoR08oCiAgICBnZW5lICAgICAgICAgID0gZmVhdHVyZXMsICAgIAogICAgT3JnRGIgICAgICAgICA9IG9yZy5Icy5lZy5kYiwKICAgIG9udCAgICAgICAgICAgPSAiQlAiLAogICAgcEFkanVzdE1ldGhvZCA9ICJCSCIsCiAgICBwdmFsdWVDdXRvZmYgID0gMC4wMSwgIyBkZWZhdWx0IDAuMDEKICAgIHF2YWx1ZUN1dG9mZiAgPSAwLjA1LCAjIGRlZmF1bHQgMC4wNQogICAgcmVhZGFibGUgPSBUUlVFLAogICAgLi4uKQogIAogIGRmdG9wcmludCA8LSBjbHVzdGVyUHJvZmlsZXI6OnNpbXBsaWZ5KGVnbylAcmVzdWx0CiAgcm93bmFtZXMoZGZ0b3ByaW50KSA8LSBOVUxMCiAgCiAgcmV0dXJuKGRmdG9wcmludCkKfQpgYGAKCmBgYHtyfQojIHBlcmZvcm0gR08gZW5yaWNobWVudApkZS5nbyA8LSBsaXN0KCkKCmZvcihpIGluIG5hbWVzKGRlLmdlbmVzKSkgewogIGRlLmdvW1tpXV0gPC0gZW5yaWNoR08yKGNlbGx0eXBlID0gaSkKfQpgYGAKCmBgYHtyfQojIHdyaXRlIEdPIGVucmljaG1lbnRzIHRvIGZpbGVzCmZvcihpIGluIHVuaXF1ZShuYW1lcyhkZS5nbykpKSB7CiAgd3JpdGUuY3N2KAogICAgZGUuZ29bW2ldXSwKICAgIGZpbGUgPSBwYXN0ZTAoIi4uL3Jlc3VsdHMvMDRfZGUtZ2VuZXMtYnktY2VsbHR5cGUvbG9nZmNfMC40MC9nby9maWxlcy9lbnJpY2hlZEdPXyIsIGksICIuY3N2IiApLAogICAgcm93Lm5hbWVzID0gRkFMU0UpCn0KYGBgCgpgYGB7cn0KZm9yKGkgaW4gbmFtZXMoZGUuZ28pKSB7CiAgcHJpbnQoaSkKICBwcmludChhcy5kYXRhLmZyYW1lKGRlLmdvW1tpXV1bMToxMCwgMl0pKQp9CmBgYAoKCgojIFZvbGNhbm8gcGxvdHMKCkJlbG93IGFyZSB2b2xjYW5vIHBsb3RzIGZvciBhbGwgY2VsbCB0eXBlczogLWxvZzEwKHAgdmFsdWUpIHZzIGxvZ0ZDOyB0b3AgMzAgZ2VuZXMgd2l0aCBoaWdoZXN0IGBhYnMobG9nRkMpYCBhcmUgbGFiZWxlZC4KCmBgYHtyfQojIGZ1bmN0aW9uIGZvciB2b2xjYW5vIHBsb3QKZGVfdm9sY2FubyA8LSBmdW5jdGlvbihjZWxsdHlwZSkgewogIAogIHBsb3QuZGF0IDwtIGRlLmdlbmVzW1tjZWxsdHlwZV1dCiAgcGxvdC5kYXQkZ2VuZSA8LSByb3duYW1lcyhwbG90LmRhdCkKICAKICAjIHRvcCBERSBnZW5lcwogIGdlbmVzLnRvLmxhYmVsID0gZGUuZ2VuZXNbW2NlbGx0eXBlXV0gJT4lIAogICAgLltvcmRlcihhYnMoZGUuZ2VuZXNbW2NlbGx0eXBlXV0kYXZnX2xvZ0ZDKSwgZGVjcmVhc2luZyA9IFRSVUUpLCBdICU+JSAKICAgIHJvd25hbWVzKCkgJT4lIC5bMTozMF0KICAKICBwbG90LmRhdCRsYWJlbCA8LSBpZmVsc2UodGVzdCA9IHBsb3QuZGF0JGdlbmUgJWluJSBnZW5lcy50by5sYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVzID0gcGxvdC5kYXQkZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbm8gPSBOQSkKICAKICBwIDwtIGdncGxvdChwbG90LmRhdCwgCiAgICAgICAgICAgICAgYWVzKGF2Z19sb2dGQywgLWxvZzEwKHBfdmFsX2FkaiksIGxhYmVsID0gbGFiZWwpKSArIAogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNCwgc2l6ZSA9IDIpICsgCiAgICBnZ3RpdGxlKGNlbGx0eXBlKSArCiAgICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAxLjksIGZvcmNlID0gMSwgY29sb3IgPSAiZ3JleTIwIikgKwogICAgdGhlbWVfYncoKSArCiAgICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjI1KSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSwKICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksCiAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkgCiAgCiAgcmV0dXJuKHApCn0KYGBgCgpgYGB7ciBmaWcuYXNwPTAuOCwgZmlnLndpZHRoPTIuNX0KZm9yKGkgaW4gbmFtZXMoZGUuZ2VuZXMpKSB7CiAgcCA8LSBkZV92b2xjYW5vKGNlbGx0eXBlID0gaSkKICAKICBnZ3NhdmUoCiAgICBwLAogICAgZmlsZW5hbWUgPSBwYXN0ZTAoCiAgICAgICIuLi9yZXN1bHRzLzA0X2RlLWdlbmVzLWJ5LWNlbGx0eXBlL2xvZ2ZjXzAuNDAvZGUvcGxvdHMvdm9sY2Fub3Bsb3RfIiwgCiAgICAgIGksICIucGRmIiksCiAgICB3aWR0aCA9IDUsIGhlaWdodCA9IDQsIHVuaXRzID0gImluIikKICAKICBwcmludChwKQp9CmBgYAoKIyBMYWJvciBzaWduYXR1cmUKVGhlIGNvbnRyb2wgc2FtcGxlcyBpbiBvdXIgZGF0YSBjb21lIGZyb20gQy1zZWN0aW9ucy4gV2hpY2ggbWFrZXMgdGhpcyBhIGNvbmZvdW5kaW5nIGZhY3RvciBpbiBvdXIgYW5hbHlzZXMuIEluIG9yZGVyIHRvIGFkZHJlc3MgdGhpcywgd2UgY2FuIGNvbXBhcmUgdGhlIGxpc3Qgb2YgREUgZ2VuZXMgYmV0d2VlbiBjb3ZpZCBhbmQgY29udHJvbCBzYW1wbGVzIHdpdGggdGhlIGxpc3Qgb2YgREUgZ2VuZXMgYmV0d2VlbiB0ZXJtIGluIGxhYm9yIChUSUwpIGFuZCB0ZXJtIG5vdCBpbiBsYWJvciAoVE5MKS4gVGhpcyBjYW4gdGVsbCB1cyBpZiAoYW5kIGhvdyBtdWNoKSBwYXJ0IG9mIG91ciBERSByZXN1bHRzIGFyZSBkcml2ZW4gYnkgbGFib3IgcmF0aGVyIHRoYW4gY292aWQgc3RhdHVzLiAKClRoZSBUSUwgdnMgVE5MIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gcmVzdWx0cyBmcm9tIFtAcGlxdWUtcmVnaV9zaW5nbGVfMjAxOV0gY2FuIGJlIHVzZWQgZm9yIHRoaXMuIEkgZG93bmxvYWRlZCB0aGUgc3VwcGxlbWVudGFyeSBmaWxlLiBUaGUgZmlsZSBpcyBkZXNjcmliZWQgYXMgZm9sbG93czogIlRoZSBjb2x1bW5zIHJlcHJlc2VudDogMSkgQ2x1c3RlciBvciBjZWxsLXR5cGUgbmFtZSwgMikgQ29tcGFyaXNvbiBncm91cHMgb3IgY29udHJhc3QgKGkuZS4sIFROTCB2cyBUSUwsIFRJTCB2cyBQVEwpLCAzKSBFbnNlbWJsIGdlbmUgaWRlbnRpZmllciwgNCkgR2VuZSBzeW1ib2wsIDUpIGJhc2VNZWFuIGdlbmUgYmFzZWxpbmUgZXhwcmVzc2lvbiBhcyBjYWxjdWxhdGVkIGJ5IERFU2VxMiwgNikgbG9nMiBGb2xkIENoYW5nZSBvZiB0aGUgZmlyc3QgZ3JvdXAgaW4gY29sdW1uIHR3byB2ZXJzdXMgdGhlIHNlY29uZCBncm91cCwgNykgU3RhbmRhcmQgZXJyb3IgZXN0aW1hdGVkIGZvciB0aGUgbG9nMiBGb2xkIENoYW5nZSwgOCkgTm9taW5hbCBwLXZhbHVlLCA5KSBxLXZhbHVlIG9yIGFkanVzdGVkIHAtdmFsdWUgdG8gY29udHJvbCBmb3IgRkRSLiBPbmx5IHJvd3Mgd2l0aCBxIDwgMC4yIGFyZSByZXBvcnRlZC4iLiAKCmBgYHtyfQp0aWwgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKCIuLi9pbmZvL2Zyb20tcGFwZXJzL3BpcXVlLXJlZ2lfMjAxOS9zdXBwL2VsaWZlLTUyMDA0LXN1cHA1LXYyLnhscyIpCmxhYm9yIDwtIHRpbCAlPiUgZmlsdGVyKENvbXBhcmUgPT0gIlROTF92X1RJTCIgJiBwYWRqIDwgMC4wNSkKaGVhZChsYWJvcikKYGBgCgpOb3cgd2UgaGF2ZSB0byBmaW5kIG91dCBob3cgbWFueSBvZiB0aGUgREUgZ2VuZXMgYmV0d2VlbiBUTkwgYW5kIFRJTCBhcmUgc2hhcmVkIHdpdGggREUgZ2VuZXMgYmV0d2VlbiBjb3ZpZCBhbmQgY29udHJvbC4gCgpgYGB7cn0KIyBhbGwgREUgZ2VuZXMgYWNyb3NzIGFsbCBjZWxsIHR5cGVzIGJldHdlZW4gY292aWQgYW5kIGNvbnRyb2wKZGUuYWxsIDwtIGRlLmdlbmVzCgpmb3IoaSBpbiBuYW1lcyhkZS5hbGwpKSB7CiAgZGUuYWxsW1tpXV0kZ2VuZSA8LSByb3duYW1lcyhkZS5hbGxbW2ldXSkKfQoKZGUuYWxsIDwtIGRvLmNhbGwocmJpbmQsIGRlLmFsbCkKCmRlLmFsbC5nZW5lcyA8LSBkZS5hbGwkZ2VuZSAlPiUgdW5pcXVlKCkKCiMgYWxsIGxhYm9yIERFIGdlbmVzCmxhYm9yLmdlbmVzIDwtIGxhYm9yJEdlbmUgJT4lIHVuaXF1ZSgpCmBgYAoKCmBgYHtyfQojIG51bWJlciBvZiBjb3ZpZCB2cyBjb250cm9sIERFIGdlbmVzIChhY3Jvc3MgYWxsIGNlbGwgdHlwZXMpCmxlbmd0aChkZS5hbGwuZ2VuZXMpCmBgYAoKYGBge3J9CiMgbnVtYmVyIG9mIFROTCB2cyBUSUwgREUgZ2VuZXMgKGFjcm9zcyBhbGwgY2VsbCB0eXBlcykKbGVuZ3RoKGxhYm9yLmdlbmVzKQpgYGAKCmBgYHtyfQojIG51bWJlciBvZiBERSBnZW5lcyBzaGFyZWQgYmV0d2VlbiBjb3ZpZCB2cyBjb250cm9sIGFuZCBUTkwgdnMgVElMCmxlbmd0aChpbnRlcnNlY3QoZGUuYWxsLmdlbmVzLCBsYWJvci5nZW5lcykpCmBgYAoKVGhlcmUgYXJlIDg0IGdlbmVzIHRoYXQgYXJlIHNoYXJlZCBiZXR3ZWVuIGNvdmlkIHZzIGNvbnRyb2wgYW5kIFROTCB2cyBUSUwuIFRoZXNlIGFyZSBsaWtlbHkgcmVsYXRlZCB0byBsYWJvci4gVGhlc2UgZ2VuZXMgYXJlIGFzIGZvbGxvd3M6CgpgYGB7cn0KaW50ZXJzZWN0KGRlLmFsbC5nZW5lcywgbGFib3IuZ2VuZXMpICU+JSBzb3J0KCkKYGBgCgpOb3cgd2UgY2FuIGNvbXBhcmUgd2hldGhlciB0aGUgc2hhcmVkIGdlbmVzIGNoYW5nZSBpbiB0aGUgc2FtZSBkaXJlY3Rpb24gdGhhdCB3ZSBleHBlY3QgYmFzZWQgb24gdGhlIGluZm9ybWF0aW9uIHRoYXQgb3VyIGNvbnRyb2wgc2FtcGxlcyBjb21lIGZyb20gQy1zZWN0aW9ucy4gCgpgYGB7cn0KIyBjcmVhdGUgYSBkYXRhZnJhbWUgb2Ygc2hhcmVkIGdlbmVzIGFuZCB0aGVpciBkaXJlY3Rpb24gb2YgY2hhbmdlcwpzaGFyZWQgPC0gZGF0YS5mcmFtZSgKICBnZW5lID0gaW50ZXJzZWN0KGRlLmFsbC5nZW5lcywgbGFib3IuZ2VuZXMpICU+JSBzb3J0KCkKKQoKc2hhcmVkJGxhYm9yX2xvZ2ZjIDwtIE5BCmZvcihpIGluIDE6bnJvdyhzaGFyZWQpKXsKICBzaGFyZWQkbGFib3JfbG9nZmNbaV0gPC0gbWVhbihsYWJvciRsb2cyRm9sZENoYW5nZVtsYWJvciRHZW5lID09IHNoYXJlZCRnZW5lW2ldXSkKfSAjIGJlY2F1c2UgdGhlIHNhbWUgZ2VuZSBjYW4gYmUgREUgaW4gbXVsdGlwbGUgY2VsbCB0eXBlcywgd2UgaGF2ZSB0byB0YWtlIGF2ZXJhZ2UgbG9nRkMuIE5vdCB0aGUgYmVzdCBvcHRpb24sIGJ1dCBxdWljayBhbmQgZGlydHkuIAoKc2hhcmVkJGNvdmlkX2xvZ2ZjIDwtIE5BCmZvcihpIGluIDE6bnJvdyhzaGFyZWQpKXsKICBzaGFyZWQkY292aWRfbG9nZmNbaV0gPC0gbWVhbihkZS5hbGwkYXZnX2xvZ0ZDW2RlLmFsbCRnZW5lID09IHNoYXJlZCRnZW5lW2ldXSkKfSAKCnNoYXJlZCRsYWJvcl9wYXR0ZXJuW3NoYXJlZCRsYWJvcl9sb2dmYyA8IDBdIDwtICJUSUxfaGlnaCIKc2hhcmVkJGxhYm9yX3BhdHRlcm5bc2hhcmVkJGxhYm9yX2xvZ2ZjID4gMF0gPC0gIlROTF9oaWdoIgoKc2hhcmVkJGNvdmlkX3BhdHRlcm5bc2hhcmVkJGNvdmlkX2xvZ2ZjIDwgMF0gPC0gImNvbnRyb2xfaGlnaCIKc2hhcmVkJGNvdmlkX3BhdHRlcm5bc2hhcmVkJGNvdmlkX2xvZ2ZjID4gMF0gPC0gImNvdmlkX2hpZ2giCgpgYGAKClRoZSBjb250aWdlbmN5IHRhYmxlIG9mIHNoYXJlZCBERSBnZW5lcyBsb29rcyBsaWtlIHRoaXM6CgpgYGB7cn0KdGFibGUoc2hhcmVkJGxhYm9yX3BhdHRlcm4sIHNoYXJlZCRjb3ZpZF9wYXR0ZXJuKQpgYGAKCk9yIGluIHRlcm1zIG9mIHByb3BvcnRpb25zLCBsaWtlIHRoaXM6CgpgYGB7cn0KdGFibGUoc2hhcmVkJGxhYm9yX3BhdHRlcm4sIHNoYXJlZCRjb3ZpZF9wYXR0ZXJuKSAlPiUgcHJvcC50YWJsZSgpCmBgYAoKRmlzaGVyJ3MgZXhhY3QgdGVzdCBmb3IgdGhpcyBjb250aW5nZW5jeSB0YWJsZSBsb29rcyBsaWtlIHRoaXM6CgpgYGB7cn0KdGFibGUoc2hhcmVkJGxhYm9yX3BhdHRlcm4sIHNoYXJlZCRjb3ZpZF9wYXR0ZXJuKSAlPiUgZmlzaGVyLnRlc3QoYWx0ZXJuYXRpdmUgPSAidHdvLnNpZGVkIikKYGBgCgpMYWJvciBpcyBsaWtlbHkgYSBjb25mb3VuZGluZyBmYWN0b3IgaW4gb3VyIGFuYWx5c2lzLCBidXQgdGhlIGVmZmVjdCBpcyB2ZXJ5IG1pbGQsIGlmIGFueS4gU2luY2Ugb3VyIGNvbnRyb2wgc2FtcGxlcyBhcmUgZnJvbSBDLXNlY3Rpb25zIChpLmUuIG5vdCBpbiBsYWJvciksIGJ1dCBjb3ZpZCBzYW1wbGVzIGFyZSBhdCB0ZXJtIGFuZCBpbiBsYWJvciAobm90IEMtc2VjdGlvbiksIGlmIGxhYm9yIGlzIGEgY29uZm91bmRpbmcgZmFjdG9yLCB3ZSBzaG91bGQgZXhwZWN0IGEgaGlnaCBwcm9wb3J0aW9uIG9mIGdlbmVzIHRvIGJlIHNoYXJlZCBiZXR3ZWVuICJjb3ZpZF9oaWdoIiBhbmQgIlRJTF9oaWdoIi4gV2Ugc2VlIHRoaXMgbG9va2luZyBhdCB0aGUgb2RkcyByYXRpbyBvbmx5LiBCdXQgdGhlIHAgdmFsdWUgZm9yIEZpc2hlcidzIGV4YWN0IHRlc3QgaXMgMC42OSwgc3VnZ2VzdGluZyB0aGF0IHRoZSBsYWJvciBhbmQgY292aWQgc3RhdHVzIGFyZSBsYXJnZWx5IGluZGVwZW5kZW50IGluIG91ciBzYW1wbGVzLiAKCldoaWxlIGl0J3MgbmljZSB0byBiZSBhd2FyZSBvZiB0aGlzIHBvdGVudGlhbCBidXQgbWlsZCBlZmZlY3QsIGl0IGlzIG5vdCBhIGJpZyBwcm9ibGVtLiBUaGlzIGVmZmVjdCBpcyBvbmx5IHJlbGV2YW50IGZvciBnZW5lcyB0aGF0IGFyZSBzaGFyZWQgYmV0d2VlbiB0aGUgdHdvIGNvbXBhcmlzb25zLS0tVE5MIHZzIFRJTCBhbmQgY292aWQgdnMgY29udHJvbC4gVGhhdCBmcmFjdGlvbiBvZiBnZW5lcywgdG8gYmVnaW4gd2l0aCwgaXMgc21hbGwuIEFyb3VuZCAxOTUgZ2VuZXMgYXJlIERFIGJldHdlZW4gVElMIGFuZCBUTkwuIE9mIHRoZXNlIDg0IGFyZSBhbHNvIERFIGJldHdlZW4gY292aWQgYW5kIGNvbnRyb2wsIGxpa2VseSByZXByZXNlbnRpbmcgdGhlICJsYWJvciBzaWduYXR1cmUiIHJhdGhlciB0aGFuICJjb3ZpZCBzaWduYXR1cmUiLiBCdXQgdGhlIG51bWJlciBvZiBnZW5lcyB0aGF0IGFyZSBERSBiZXR3ZWVuIGNvdmlkIGFuZCBjb250cm9sIGlzIG11Y2ggbGFyZ2VyICgxMjY3KSwgaS5lLiBvdmVyIDEwMDAgZ2VuZXMgYXJlIERFIGJldHdlZW4gY292aWQgYW5kIGNvbnRyb2wgdGhhdCBhcmUgbm90IERFIGJldHdlZW4gVE5MIGFuZCBUSUwuIFRoZXNlIG1vc3QgbGlrZWx5IHJlcHJlc2VudCB0aGUgImNvdmlkIHNpZ25hdHVyZSIgdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbi4gCgojIERhdGEtdmlzIGlkZWFzIGZvciB0aGUgcGFwZXIKCiMjIERFIGdlbmVzIGRvdHBsb3QKCldlIG5lZWQgdG8gZmluZCBhIGdvb2Qgd2F5IHRvIHByZXNlbnQgREUgZ2VuZXMuIFZpb2xpbiBwbG90cyBhcmUgbmljZSBpZiB0aGUgbnVtYmVyIG9mIHRvIHNob3cgaXMgc21hbGwuIElmIHdlIHdhbnQgdG8gc2hvdyBhIHNtYWxsIHNldCBvZiBnZW5lcywgd2UgY2FuIHVzZSB2aW9saW4gcGxvdHMuIAoKQW5vdGhlciBvcHRpb24gaXMgZG90cGxvdHMuIFRoZSBhZHZhbnRhZ2Ugb2YgZG90cGxvdHMgaXMgdGhhdCB0aGV5IGFyZSBtdWNoIG1vcmUgIHNwYWNlLWVmZmljaWVudCwgd2hpY2ggaXMgZGVzaXJhYmxlIGZvciBwYXBlciBmaWd1cmVzLiBJbiBhIDZpbng4aW4gZmlndXJlLCB3ZSBjYW4gY29tZm9ydGFibHkgc2hvdyBkYXRhIGZvciBhYm91dCA1MCBnZW5lcyBpbiBhbGwgY2VsbCB0eXBlcyBzZXBhcmF0ZWQgYnkgY292aWQgc3RhdHVzLiAKClRoZSBgU2V1cmF0OjpEb3RQbG90YCBmdW5jdGlvbiBoYXMgc29tZSBwcm9ibGVtcywgdGhvdWdoLCB0aGF0IG1ha2UgaXMgbm90IHJlYWxseSB1c2FibGUgZm9yIGdvb2QgcXVhbGl0eSBwbG90dGluZy4gT25lIGJpZyBwcm9ibGVtIGlzIGhvdyBpdCBoYW5kbGVzIHNwbGl0dGluZyBzYW1wbGVzIGJ5IHRyZWF0bWVudCBhbmQgY29udHJvbCAoY292aWQgYW5kIGNvbnRyb2wgaW4gb3VyIGNhc2UpIGluIGBzcGxpdC5ieWAgYXJndW1lbnQuIEl0IGRvZXMgc3BsaXQgeW91ciBjZWxsIHR5cGVzIGJ5IHRyZWF0bWVudCBzdGF0dXMgc28gdGhhdCB0aGUgYXZlcmFnZSBleHByZXNzaW9uIGJ5IHRyZWF0bWVudCBzdGF0dXMgZm9yIGVhY2ggY2x1c3RlciBjYW4gYmUgdmlzdWFsaXplZCwgYnV0IGl0IHVzZXMgdHdvIGRpZmZlcmVudCBjb2xvdXJzIGZvciBjb250cm9sIGFuZCB0cmVhdG1lbnQuIFRoaXMgY2F1c2VzIHR3byBwcm9ibGVtcy4gRmlyc3QsIGBnZ3Bsb3RgIGRvZXNuJ3Qgc3VwcG9ydCBtdWx0aXBsZSBjb2xvdXIgc2NhbGVzLCBhbmQgYFNldXJhdGAsIGluc3RlYWQgb2YgZmluZGluZyBhIHdheSBhcm91bmQgdGhlIGlzc3VlLCBoYXMgY2hvc2VuIHRvIG5vdCBzaG93IHRoZSBjb2xvdXJiYXIgZ3VpZGUgYXQgYWxsIHdoZW4gYHNwbGl0LmJ5YCBpcyB1c2VkIChmb3IgbW9yZSBvbiB0aGlzIGFuZCBhbm90aGVyIHJlbGF0ZWQgYnVnLCBzZWUgaHR0cHM6Ly9naXRodWIuY29tL3NhdGlqYWxhYi9zZXVyYXQvaXNzdWVzLzI0ODcgYW5kIGh0dHBzOi8vZ2l0aHViLmNvbS9zYXRpamFsYWIvc2V1cmF0L2lzc3Vlcy8yMzQyLikuIFNlY29uZCwgdXNpbmcgdHdvIGNvbG9ycyBmb3IgdmFsdWVzIChhdmcuZXhwKSBvbiB0aGUgc2FtZSBzY2FsZSBpcyBhIGJhZCBkZXNpZ24gY2hvaWNlLCBlc3BlY2lhbGx5IHdoZW4gdGhlIHBvaW50IGlzIHRvIGNvbXBhcmUgdGhlbSBzaWRlIGJ5IHNpZGUtLS1pbnRlbnNpdGllcyBmb3IgdHdvIGNvbG91cnMgbWFwcGluZyB0byB0aGUgZXhhY3Qgc2FtZSBhdmcuZXhwIHZhbHVlIGNhbiBiZSBwZXJjZWl2ZWQgZGlmZmVyZW50bHkganVzdCBiZWNhdXNlIHRoZXkgYXJlIHR3byBkaWZmZXJlbnQgY29sb3JzLiAKCgoxLiBXZSBoYXZlIHRvIGRvIGEgd29yay1hcm91bmQgdG8gcGxvdCB0aGUgZG90cGxvdHMgc3BsaXQgYnkgY292aWQgc3RhdHVzLCBwbG90dGluZyB3aXRoIGEgc2luZ2xlIGNvbG9yLiBXZSBqdXN0IHVzZSBgZ2dwbG90YCBkaXJlY3RseSB0byBtYWtlIHRoZSBkb3RwbG90cywgd2l0aCBjb3ZpZCBzdGF0dXMgb24gb25lIGF4aXMsIGdlbmVzIG9uIGFub3RoZXIsIGFuZCBmYWNldGVkIGJ5IGNlbGwgdHlwZS4gVGhpcyByZXF1aXJlcyB1cyB0byBkbyB0aGUgY2FsY3VsYXRpb25zIGZvciBhdmcuZXhwLnNjYWxlZCAgYW5kIHBjdC5leHAgb3Vyc2VsdmVzLiBGb3IgdGhpcyB3ZSBjYW4gY2hlYXQtLS13ZSBjYW4gcnVuIGBTZXVyYXQ6OmRvdHBsb3RgIHdpdGggY2VsbHR5cGVfY292aWQgYXMgaWRlbnRzIHNvIGl0IGNhbGN1bGF0ZSB0aG9zZSB2YWx1ZXMgc3BsaXQgYnkgY292aWQgc3RhdHVzIGZvciBlYWNoIGNlbGwgdHlwZSwgYnV0IGluc3RlYWQgb2YgcGxvdHRpbmcgaXQsIHdlIGNhbiBqdXN0IGV4dHJhY3QgdGhlIGAkZGF0YWAgc2xvdCBmcm9tIHRoZSByZXN1bHRpbmcgYGdncGxvdGAgb2JqZWN0IGFuZCB1c2UgaXQgZm9yIG91ciBjdXN0b20gcGxvdHRpbmcuIAoyLiBXZSBjYW4gYWxzbyBzaG93IHJlc3VsdCBvZiBERSBhbmFseXNpcyBvbiB0b3Agb2YgdGhpcyBwbG90LiBXZSBjYW4gYWRkIGEgbGluZSBzZWdtZW50IGNvbm5lY3RpbmcgdGhlIGNvdmlkIGFuZCBjb250cm9sIGRvdHMgaW4gdGhvc2UgY2VsbCB0eXBlcyBpbiB3aGljaCB0aGUgcCB2YWx1ZSBvZiBERSB0ZXN0IG9mIGJlbG93IHRocmVzaG9sZC4gCjMuIEZvciBnZW5lIG9yZGVyLCB3ZSBjYW4gdXNlIGBoY2x1c3RgIGZvciBjbHVzdGVyaW5nLCBhbmQgdXNlIHRoYXQgb3JkZXIgZm9yIHNvcnRpbmcgZ2VuZXMuIFRoaXMgd2F5LCBnZW5lcyB0aGF0IGhhdmUgc2ltaWxhciBleHByZXNzaW9uIHBhdHRlcm5zIGFyZSBwb3NpdGlvbmVkIGNsb3NlIHRvIGVhY2ggb3RoZXIsIGFuZCB0aGVyZSBpcyBzb21lIHN0cnVjdHVyZSB0byB0aGUgcGxvdHRpbmcgb3JkZXIuIAoKSGVyZSBmb3IgZGVtbywgd2UnbGwgdXNlIHRvcCA1MCBERSBnZW5lcyBmcm9tIGRlYy5BUEMuCgoKYGBge3J9CiMgdGhlbWUgZm9yIERFIGdlbmVzIGZhY2V0ZWQgZG90cGxvdAp0aGVtZV9kb3RwbG90IDwtIHRoZW1lKAogIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgdGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJCbGFjayIpLAogIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgc2l6ZSA9IDAuMSwgY29sb3IgPSAiZ3JleTYwIiksCiAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDYsIGNvbG9yID0gIkJsYWNrIiksCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYsIGNvbG9yID0gIkJsYWNrIiksCiAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjEsIGNvbG9yID0gIkJsYWNrIiksCiAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMCwgdmp1c3QgPSAwLjUsIHNpemUgPSA2LjUsIGNvbG9yID0gIkJsYWNrIiksCiAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgbGVnZW5kLmJveC5zcGFjaW5nID0gdW5pdCgwLjEsICJsaW5lcyIpLAogIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogIGxlZ2VuZC50aXRsZS5hbGlnbiA9IDAsCiAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDUuNSksCiAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjUsICJsaW5lcyIpLAogIGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCksCiAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoMC4xLCAibGluZXMiKSwKICBwYW5lbC5zcGFjaW5nLnggPSB1bml0KDAsICJsaW5lcyIpCikKYGBgCgpgYGB7cn0KIyBleHRyYWN0IHBsb3QgZGF0YSBmcm9tIFNldXJhdDo6RG90UGxvdApJZGVudHMoc2V1cikgPC0gc2V1ciRjZWxsdHlwZV9jb3ZpZApwIDwtIFNldXJhdDo6RG90UGxvdChvYmplY3QgPSBzZXVyLCBhc3NheSA9ICJTQ1QiLCAKICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSByb3duYW1lcyhkZS5nZW5lcyRkZWMuQVBDKVsxOjUwXSkKCnAuZGF0IDwtIHAkZGF0YQoKcC5kYXQkY2VsbHR5cGUgPC0gZ3N1YihwLmRhdCRpZCwgcGF0dGVybiA9ICIoLispXyhbYS16XXs1fSkiLCByZXBsYWNlbWVudCA9ICJcXDEiKQpwLmRhdCRjb3ZpZCA8LSBnc3ViKHAuZGF0JGlkLCBwYXR0ZXJuID0gIiguKylfKFthLXpdezV9KSIsIHJlcGxhY2VtZW50ID0gIlxcMiIpCmBgYAoKYGBge3J9CiMgY2x1c3RlcmluZyBmb3Igb3JkZXJpbmcKY2RhdCA8LSBwaXZvdF93aWRlcihkYXRhID0gIHAuZGF0LCBpZF9jb2xzID0gMywgbmFtZXNfZnJvbSA9IGlkLCB2YWx1ZXNfZnJvbSA9IGF2Zy5leHAuc2NhbGVkKQoKY2RhdCA8LSBhcy5kYXRhLmZyYW1lKGNkYXQpCnJvd25hbWVzKGNkYXQpIDwtIGNkYXQkZmVhdHVyZXMucGxvdApjZGF0JGZlYXR1cmVzLnBsb3QgPC0gTlVMTAoKY29yLm1hdHJpeCA8LSBjb3IodChjZGF0KSkKCiMgcmVvcmRlciBjb3JyZWxhdGlvbiBtYXRyaXggYmFzZWQgb24gY2x1c3RlcmluZwpkZCA8LSBhcy5kaXN0KCgxIC0gY29yLm1hdHJpeCkpIApoYyA8LSBoY2x1c3QoZGQsIG1ldGhvZCA9ICdjb21wbGV0ZScpCmNkYXQgIDwtIGNkYXRbaGMkb3JkZXIsIF0KCnAuZGF0JGZlYXR1cmVzLnBsb3QgPC0gZmFjdG9yKHAuZGF0JGZlYXR1cmVzLnBsb3QsIGxldmVscyA9IHJvd25hbWVzKGNkYXQpKQpgYGAKCgpgYGB7cn0KIyBwcmVwYXJlIGFubm90YXRpb24gZGF0YSB0byBkaXNwbGF5IERFIHN0YXRzLiBUaGlzIGdpdmVzIGEgZGYgb2Ygb25seSB0aGUgY29tYmluYXRpb24gb2YgY2VsbHR5cGVzIGFuZCBnZW5lcyB3aGVyZSBhZGoucCA8IDAuMDUKCmRlLmFubiA8LSBkZS5nZW5lcwoKZm9yKGkgaW4gbmFtZXMoZGUuYW5uKSkgewogIGRlLmFubltbaV1dJGdlbmUgPC0gcm93bmFtZXMoZGUuYW5uW1tpXV0pCiAgZGUuYW5uW1tpXV0kY2VsbHR5cGUgPC0gaQp9ICMgYWRkIGdlbmUgbmFtZXMgYXMgY29sdW1uLiB0aGUgY2VsbHR5cGUgaW5mbyB0b28sIHNvIHRoYXQgd2UgY2FuIHJiaW5kIGFsbCBsaXN0IGl0ZW1zLgoKZGUuYW5uIDwtIGRvLmNhbGwocmJpbmQsIGRlLmFubikKZGUuYW5uIDwtIHN1YnNldChkZS5hbm4sIGdlbmUgJWluJSBwLmRhdCRmZWF0dXJlcy5wbG90KQpkZS5hbm4kZGlmZi5leHAgPC0gZGUuYW5uJHBfdmFsX2FkaiA8IDAuMDUKZGUuYW5uJGRpZmYuZXhwIDwtIHRvbG93ZXIoYXMuY2hhcmFjdGVyKGRlLmFubiRkaWZmLmV4cCkpCgpkZS5hbm4kcF92YWwgPC0gTlVMTApkZS5hbm4kYXZnX2xvZ0ZDIDwtIE5VTEwKZGUuYW5uJHBjdC5jb3ZpZCA8LSBOVUxMCmRlLmFubiRwY3QuY250cmwgPC0gTlVMTApyb3duYW1lcyhkZS5hbm4pIDwtIE5VTEwKYGBgCgoKYGBge3IgZmlnLmFzcD0xLjEsIGZpZy53aWR0aD0zfQpxIDwtIGdncGxvdChkYXRhID0gcC5kYXQsIGFlcyh4ID0gY292aWQsIHkgPSBmZWF0dXJlcy5wbG90KSkgKwogIGdlb21fcG9pbnQoYWVzKGZpbGwgPSBhdmcuZXhwLnNjYWxlZCwgc2l6ZSA9IHBjdC5leHApLCBzaGFwZSA9IDIxLCBzdHJva2UgPSAwLCAKICAgICAgICAgICAgIGNvbG9yID0gIldoaXRlIikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIldoaXRlIiwgaGlnaCA9ICJSZWQiLCBuYW1lID0gImF2Zy5leHBcbihzY2FsZWQpIikgKwogIHNjYWxlX3hfZGlzY3JldGUoYnJlYWtzID0gYygiY250cmwiLCAiY292aWQiKSwgCiAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJDb250cm9sIiwgIkNvdmlkIikpICsKICBzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSAzLjUsCiAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKAogICAgICAgICAgICAgICAgICAgIG92ZXJyaWRlLmFlcyA9IGxpc3QoCiAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJXaGl0ZSIsIAogICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJCbGFjayIpKQogICAgICAgICAgICAgICAgICApICsKICBmYWNldF9ncmlkKC4gfiBjZWxsdHlwZSkgKwogIHVuZ2V2aXo6Omdlb21faHBsaW5lKGRhdGEgPSBkZS5hbm4sCiAgICAgICAgICAgICAgICAgICAgICAgYWVzKHkgPSBnZW5lLCB4ID0gMS41LCBjb2xvciA9IGRpZmYuZXhwKSwKICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMC4zNSwgd2lkdGggPSAxLCBsaW5lZW5kID0gImJ1dHQiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKGJyZWFrcyA9ICJ0cnVlIiwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gIjwgMC4wNSIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9ICJCbGFjayIsCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiREUgYWRqLnAiKSArCiAgdGhlbWVfZG90cGxvdAoKCmNvd3Bsb3Q6Omdnc2F2ZTIocSwgCiAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSAiLi4vcmVzdWx0cy8wNF9kZS1nZW5lcy1ieS1jZWxsdHlwZS9sb2dmY18wLjQwL2RlL3Bsb3RzL2RvdHBsb3RfdG9wNTAtZGVjLkFQQy5wZGYiLCAKICAgICAgICAgICAgICAgICB3aWR0aCA9IDYsIGhlaWdodCA9IDcsIHVuaXRzID0gImluIikKCnByaW50KHEpCmBgYAoKIyMgR08gZW5yaWNobWVudCBkb3RwbG90CgpXZSBhbHNvIGhhdmUgdG8gZmluZCBhIHdheSB0byB2aXN1YWxpemUgdGhlIEdPIGVucmljaG1lbnQgcmVzdWx0cy4gVGhpcyBvbmUgaXMgdHJpY2t5LCBidXQgZG90cGxvdCBvZiBlbnJpY2htZW50IGFuZCBwIHZhbHVlIGlzIGEgZ29vZCBvcHRpb24uIEJ1dCB0aGVyZSBhcmUgdG9vIG1hbnkgR08gY2F0ZWdvcmllcyB0aGF0IGVucmljaGVkIGFjcm9zcyBhbGwgY2VsbHR5cGVzIChhcm91bmQgMTAwMCksIHNvIHdlIGRvIG5vdCB0byBmaWx0ZXIgdGhlbSBzb21laG93LgoKIyMjIENhbGN1bGF0ZSBlbnJpY2htZW50IAoKRmlyc3Qgd2UgcHV0IGFsbCBHTyBlbnJpY2htZW50IHJlc3VsdHMgaW4gdGhlIHNhbWUgZGF0YWZyYW1lIGFuZCB0aGVuIGNhbGN1bGF0ZSB0aGUgZW5yaWNobWVudC4gSGVyZSB0aGUgR08gY2F0ZWdvcmllcyB3aWxsIGJlIHJlcGVhdGVkIGJlY2F1c2UgdGhlIHNhbWUgR08gY2F0ZWdvcnkgY2FuIGJlIGVucmljaGVkIGluIG11bHRpcGxlIGNlbGwgdHlwZXMsIGJ1dCB0aGV5IHdpbGwgb2YgY291cnNlIGhhdmUgZGlmZmVyZW50IGVucmljaG1lbnQgYW5kIHAgdmFsdWVzIGluIGVhY2ggY2VsbCB0eXBlLiAKCmBgYHtyfQojIHByZXBhcmUgZGF0YQpnby5wZGF0IDwtIGRlLmdvW2xhcHBseShkZS5nbywgbnJvdykgPiAwXQoKZm9yKGkgaW4gbmFtZXMoZ28ucGRhdCkpIHsKICBnby5wZGF0W1tpXV0kY2VsbHR5cGUgPC0gaQp9Cgpnby5wZGF0IDwtIGRvLmNhbGwocmJpbmQsIGdvLnBkYXQpCgpnby5wZGF0JHB2YWx1ZSA8LSBOVUxMCmdvLnBkYXQkZ2VuZUlEIDwtIE5VTEwKcm93bmFtZXMoZ28ucGRhdCkgPC0gTlVMTAoKIyBjYWxjdWxhdGUgZW5yaWNobWVudApnby5wZGF0JGIgPC0gYXMubnVtZXJpYyhzYXBwbHkoCiAgICBzdHJzcGxpdChnby5wZGF0JEdlbmVSYXRpbywgc3BsaXQgPSAiLyIpLCBNQVJHSU4gPSAxLCBGVU4gPSAnW1snKSkKCmdvLnBkYXQkbiA8LSBhcy5udW1lcmljKHNhcHBseSgKICAgIHN0cnNwbGl0KGdvLnBkYXQkR2VuZVJhdGlvLCBzcGxpdCA9ICIvIiksIE1BUkdJTiA9IDIsIEZVTiA9ICdbWycpKQoKZ28ucGRhdCRCIDwtIGFzLm51bWVyaWMoc2FwcGx5KAogICAgc3Ryc3BsaXQoZ28ucGRhdCRCZ1JhdGlvLCBzcGxpdCA9ICIvIiksIE1BUkdJTiA9IDEsIEZVTiA9ICdbWycpKQoKZ28ucGRhdCROIDwtIGFzLm51bWVyaWMoc2FwcGx5KAogICAgc3Ryc3BsaXQoZ28ucGRhdCRCZ1JhdGlvLCBzcGxpdCA9ICIvIiksIE1BUkdJTiA9IDIsIEZVTiA9ICdbWycpKQoKZ28ucGRhdCRlbnJpY2htZW50IDwtIChnby5wZGF0JGIgLyBnby5wZGF0JG4pIC8gKGdvLnBkYXQkQiAvIGdvLnBkYXQkTikKCmhlYWQoZ28ucGRhdCkKYGBgCiMjIyBQbG90dGluZyBmdW5jdGlvbgoKU2luY2UgdGhlIHNhbWUgR08gY2F0ZWdvcmllcyBjYW4gYmUgZW5yaWNoZWQgaW4gZGlmZmVyZW50IGNlbGx0eXBlcywgdG8gYXZvaWQgcmVwZXRpdGlvbiBhbmQgdG8gZW5hYmxlIHNlZWluZyBwYXR0ZXJucyBvZiBlbnJpY2htZW50IGFjcm9zcyBjZWxsdHlwZXMsIHdlIHdhbnQgdG8gdmlzdWFsaXplIGVucmljaG1lbnQgb3V0cHV0IG5vdCBpbmRpdmlkdWFsbHkgYnkgY2VsbHR5cGUsIGJ1dCBpbiBhIGNvbWJpbmVkIHBsb3QgZm9yIGFsbCBjZWxsdHlwZXMuIFdlIGNhbiBkb3RwbG90IGNlbGx0eXBlIG9uIG9uZSBheGlzLCBhbmQgR08gY2F0ZWdvcnkgb24gYW5vdGhlci4gQ29sb3Igb2YgdGhlIGRvdCByZXByZXNlbnRzIHAgdmFsdWUgYW5kIHNpemUgcmVwcmVzZW50cyBlbnJpY2htZW50LiBXZSBjYW4gY2x1c3RlciB0aGUgR08gY2F0ZWdvcmllcyBieSBhbmQgb3JkZXIgdGhlbSBhY2NvcmRpbmdseSBzbyB0aGF0IHdlIGNhbiBlYXNpbHkgc2VlIHRoZSBwYXR0ZXJucyBhY3Jvc3MgY2VsbHR5cGVzLiAKCmBgYHtyfQojIHRoZW1lIGZvciBHTyBkb3RwbG90CnRoZW1lX0dPIDwtIHRoZW1lKAogIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgdGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJCbGFjayIpLAogIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuMTUsIGNvbG9yID0gIkdyZXk5MCIpLAogIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuMjUsIGNvbG9yID0gIkJsYWNrIiksCiAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDYsIGNvbG9yID0gIkJsYWNrIiksCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYsIGNvbG9yID0gIkJsYWNrIiksCiAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjEsIGNvbG9yID0gIkJsYWNrIiksCiAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMCwgdmp1c3QgPSAwLjUsIHNpemUgPSA2LjUsIGNvbG9yID0gIkJsYWNrIiksCiAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwKICBsZWdlbmQuYm94ID0gInZlcnRpY2FsIiwKICBsZWdlbmQuYm94LnNwYWNpbmcgPSB1bml0KDAuMiwgImxpbmVzIiksCiAgbGVnZW5kLmJveC5qdXN0ID0gImxlZnQiLAogIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgbGVnZW5kLnRpdGxlLmFsaWduID0gMCwKICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNS41KSwKICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNSwgImxpbmVzIiksCiAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSwKICBsZWdlbmQuc3BhY2luZy55ID0gdW5pdCgwLjIsICJsaW5lcyIpLAopCmBgYAoKYGBge3J9CiMgZnVuY3Rpb24KCmRvdHBsb3RfZ28gPC0gZnVuY3Rpb24oZ28ucGRhdC5zdWIpIHsKICAKICAjIGNsdXN0ZXJpbmcgZm9yIG9yZGVyaW5nCiAgY2RhdCA8LSBwaXZvdF93aWRlcihkYXRhID0gZ28ucGRhdC5zdWIsIGlkX2NvbHMgPSAyLCAKICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSBjZWxsdHlwZSwgdmFsdWVzX2Zyb20gPSBwLmFkanVzdCkKICBjZGF0WywgMjpuY29sKGNkYXQpXSA8LSBhcHBseShjZGF0WywgMjpuY29sKGNkYXQpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTUFSR0lOID0gMiwgRlVOID0gZnVuY3Rpb24oeCl7cmVwbGFjZV9uYSh4LCAxKX0pCiAgCiAgY2RhdCA8LSBhcy5kYXRhLmZyYW1lKGNkYXQpCiAgcm93bmFtZXMoY2RhdCkgPC0gY2RhdCREZXNjcmlwdGlvbgogIGNkYXQkRGVzY3JpcHRpb24gPC0gTlVMTAogIAogIGNvci5tYXRyaXggPC0gY29yKHQoY2RhdCkpCiAgCiAgZGQgPC0gYXMuZGlzdCgoMSAtIGNvci5tYXRyaXgpKSAKICBoYyA8LSBoY2x1c3QoZGQsIG1ldGhvZCA9ICdjb21wbGV0ZScpCiAgCiAgY2RhdCA8LSBjZGF0W2hjJG9yZGVyLCBdCiAgCiAgZ28ucGRhdC5zdWIkRGVzY3JpcHRpb24gPC0gZmFjdG9yKGdvLnBkYXQuc3ViJERlc2NyaXB0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gcm93bmFtZXMoY2RhdCkpCiAgIyBwbG90CiAgcCA8LSBnZ3Bsb3QoZGF0YSA9IGdvLnBkYXQuc3ViLAogICAgICAgICAgICAgIGFlcyh4ID0gY2VsbHR5cGUsIHkgPSBEZXNjcmlwdGlvbikpICsKICAgIGdlb21fcG9pbnQoYWVzKHNpemUgPSBlbnJpY2htZW50LCBjb2xvciA9IC1sb2cxMChwLmFkanVzdCkpKSArCiAgICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3cgPSBjb2xvclJhbXBQYWxldHRlKGMoIldoaXRlIiwgIkJsdWUiKSkoMTApWzVdLAogICAgICAgICAgICAgICAgICAgICAgICAgaGlnaCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiV2hpdGUiLCAiQmx1ZSIpKSgxMClbMTBdKSArCiAgICBzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSAzLjUpICsKICAgIHRoZW1lX0dPCiAgCiAgcmV0dXJuKHApCn0KCmBgYAoKCiMjIyBEb3RwbG90IHdpdGhvdXQgcmVkdW5kYW5jeSBmaWx0ZXJpbmcKCkhlcmUsIGFmdGVyIGhhdmluZyBjb21iaW5lZCBhbGwgZW5yaWNoZWQgR08gY2F0ZWdvcmllcyBmcm9tIGFsbCBjZWxsIHR5cGVzLCB3ZSBzb3J0IHRoZW0gYnkgcCB2YWx1ZSBhbmQgcGljayB0aGUgdG9wIDEwMCwgcmVnYXJkbGVzcyBvZiB3aGljaCBjZWxsIHR5cGUgdGhleSBjb21lIGZyb20uIFRoaXMgaXMgcGVyaGFwcyBub3QgdGhlIGJlc3Qgd2F5IHRvIGZpbHRlciwgYnV0IG9uZSBvcHRpb24gdGhhdCBwaWNrcyB1cCB0aGUgbW9zdCBoaWdobHkgZW5yaWNoZWQgY2F0ZWdvcmllcyBhY3Jvc3MgbW9zdCBjZWxsdHlwZXMuIEJ1dCBpdCBkb2VzIG1pc3Mgc29tZSBjZWxsdHlwZXMgd2hvc2UgZW5yaWNoZWQgR08gY2F0ZWdvcmllcyBhcmUgbm90IGluIHRoZSB0b3AgMTAwLgoKYGBge3IgZmlnLmFzcD0xLCBmaWcud2lkdGg9Mi41fQojIHBsb3R0aW5nIHN1YnNldApnby5zdWIgPC0gZ28ucGRhdFtvcmRlcihnby5wZGF0JHAuYWRqdXN0LCBkZWNyZWFzaW5nID0gRkFMU0UpWzE6MTAwXSwgXQoKcCA8LSBkb3RwbG90X2dvKGdvLnBkYXQuc3ViID0gZ28uc3ViKQoKZ2dzYXZlKAogIHAsIAogIGZpbGVuYW1lID0gIi4uL3Jlc3VsdHMvMDRfZGUtZ2VuZXMtYnktY2VsbHR5cGUvbG9nZmNfMC40MC9nby9wbG90cy9nb19kb3RwbG90X3RvcDEwMC5wZGYiLAogIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSwgdW5pdHMgPSAiaW4iCikKCnByaW50KHApCmBgYAoKIyMjIFBsb3R0aW5nIGFmdGVyIFJFVklHTwoKUkVWaUdPIChodHRwOi8vcmV2aWdvLmlyYi5oci8pIGlzIGFuIG9ubGluZSB0b29sIHRoYXQgdGFrZXMgYSBsaXN0IG9mIEdPIHRlcm1zIGFuZCBwIHZhbHVlcyBhcyBpbnB1dCBhbmQgb3V0cHV0cyBhIHJlZHVjZWQgbGlzdCBvZiBHTyBjYXRlZ29yaWVzLiBJdCBkb2VzIHRoaXMgYnkgZmluZGluZyBHTyB0ZXJtcyB0aGF0IGFyZSBzaW1pbGFyIHRvIGVhY2ggb3RoZXIsIGFuZCBiYXNlZCBvbiBzaGFyZWQgZ2VuZXMsIGl0IGFzc2lnbnMgInVuaXF1ZW5lc3MiIGFuZCAiZGlzcGVuc2FiaWxpdHkiIHNjb3JlcyBmb3IgZWFjaCBHTyBjYXRlZ29yeS4gV2UgY2FuIGZpbHRlciBvdXQgR08gY2F0ZWdvZWlzIHRoYXQgaGF2ZSBoaWdoIGRpc3BlbnNhYmlsaXR5IHNjb3JlLS0tdGhlc2UgR08gY2F0ZWdvcmllcyBhcmUgcmVkdW5kYW50IGJlY2F1c2UgdGhleSBoYXZlIGJlZW4gcmVwcmVzZW50ZWQgYnkgb3RoZXIgR08gY2F0ZWdvcmllcyBpbiB0aGUgbGlzdC4gCgpgYGB7cn0KIyB3cml0ZSBmaWxlIGFuZCBydW4gaXQgb24gcmV2aWdvCndyaXRlLnRhYmxlKAogIGdvLnBkYXRbLCBjKCJJRCIsICJwLmFkanVzdCIpXSwgCiAgZmlsZSA9ICIuLi9yZXN1bHRzLzA0X2RlLWdlbmVzLWJ5LWNlbGx0eXBlL2xvZ2ZjXzAuNDAvZ28vcmV2aWdvL0dPX2Zvci1yZXZpZ28udHh0IiwgCiAgcXVvdGUgPSBGQUxTRSwgc2VwID0gIlx0Iiwgcm93Lm5hbWVzID0gRkFMU0UsIGNvbC5uYW1lcyA9IFRSVUUpCmBgYAoKCmBgYHtyfQpyZXZpZ28gPC0gcmVhZC5jc3YoIi4uL3Jlc3VsdHMvMDRfZGUtZ2VuZXMtYnktY2VsbHR5cGUvbG9nZmNfMC40MC9nby9yZXZpZ28vUkVWSUdPLmNzdiIpCmBgYAoKCmBgYHtyIGZpZy53aWR0aD0yLjg1LCBmaWcuYXNwPTEuMn0KIyBwbG90dGluZyBzdWJzZXQKdG8ua2VlcCA8LSByZXZpZ28kdGVybV9JRFtyZXZpZ28kZGlzcGVuc2FiaWxpdHkgPCAwLjg1XQpnby5zdWIgPC0gZ28ucGRhdFtnby5wZGF0JElEICVpbiUgdG8ua2VlcCwgXQoKcCA8LSBkb3RwbG90X2dvKGdvLnBkYXQuc3ViID0gZ28uc3ViKQoKZ2dzYXZlKAogIHAsIAogIGZpbGVuYW1lID0gIi4uL3Jlc3VsdHMvMDRfZGUtZ2VuZXMtYnktY2VsbHR5cGUvbG9nZmNfMC40MC9nby9wbG90cy9nb19kb3RwbG90X3Jldmlnby5wZGYiLAogIHdpZHRoID0gNS43NSwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iCikKCnByaW50KHApCgpgYGAKCiMjIyBQbG90dGluZyBhZnRlciBSRVZpR086IG9ubHkgdGVybWluYWwgbm9kZXMgb2YgREFHCgpHTyB0ZXJtcyBhcmUgaGllcmFyY2hpY2FsbHkgb3JnYW5pemVkIGluIGEgZGlyZWN0ZWQgYWN5Y2xpYyBncmFwaCAoREFHKS4gU29tZXRpbWVzIHlvdSBjYW4gaGF2ZSB0d28gdGVybXMgZW5yaWNoZWQgd2hlcmUgb25lIGlzIG1vcmUgaW5jbHVzaXZlIHRoYW4gdGhlIG90aGVyLCBhbmQgdGhlIGxlc3MgaW5jbHVzaXZlIG9uZSBpcyB0aGUgbW9yZSBpbmZvcm1hdGl2ZSBvbmUuIEZvciBleGFtcGxlLCBpZiB5b3UgaGF2ZSAiY2VsbCBraWxsaW5nIiBhbmQgIm5lZ2F0aXZlIHJlZ3VsYXRpb24gb2YgY2VsbCBraWxsaW5nIiBhcyB0d28gZW5yaWNoZWQgR08gdGVybXMsIHRoZSBsYXR0ZXItLS10aGUgbW9yZSBpbmZvcm1hdGl2ZSBvbmUtLS1pcyBhIGNoaWxkIHRlcm0gb2YgdGhlIHRoZSBmb3JtZXIuIFRoZSBwYXJlbnQtY2hpbGQgcmVsYXRpb25zaGlwIG9mIEdPIHRlcm1zIGlzIGFub3RoZXIgYXhpcyBhbG9uZyB3aGljaCB3ZSBjYW4gcHJ1bmUgdGhlIGxpc3Qgb2YgR08gdGVybXMsIGJ5IGRpc2NhcmRpbmcgdGVybXMgd2hvc2UgY2hpbGQgdGVybXMgYXJlIGFsc28gZW5yaWNoZWQuIAoKYGBge3J9CiMgZnVuY3Rpb24gZm9yIGV4dHJhY3RpbmcgdGVybWluYWwgbm9kZXMgKHRlcm1zIHdob3NlIGNoaWxkIHRlcm1zIGFyZSBub3QgZW5yaWNoZWQpCnRlcm1pbmFsIDwtIGZ1bmN0aW9uKHRlcm1zLCBvbnRvbG9neT1jKCJDIiwgIlAiLCAiRiIpKQp7CiAgRlVOIDwtIHN3aXRjaChtYXRjaC5hcmcob250b2xvZ3kpLCAgCiAgICAgICAgICAgICAgICBDID0gR09DQ1BBUkVOVFMsCiAgICAgICAgICAgICAgICBQID0gR09CUFBBUkVOVFMsIAogICAgICAgICAgICAgICAgRiA9IEdPTUZQQVJFTlRTKQogIHRlcm1pbmFsIDwtIHRlcm1zCiAgc2VlbiA8LSBjKHRlcm1zLCAiYWxsIikKICB3aGlsZSAobGVuZ3RoKHRlcm1zKSkgewogICAgc2VlbiA8LSBjKHRlcm1zLCBzZWVuKQogICAgdGVybXMgPC0gbWFwcGVkUmtleXMoRlVOW3Rlcm1zXSkKICAgIHRlcm1pbmFsIDwtIHRlcm1pbmFsWyF0ZXJtaW5hbCAlaW4lIHRlcm1zXQogICAgdGVybXMgPC0gdGVybXNbIXRlcm1zICVpbiUgc2Vlbl0KICB9CiAgdGVybWluYWwKfQpgYGAKCmBgYHtyfQojIHJ1biByZXZpZ28gb3V0cHV0IHRlcm1zIHRocm91Z2ggdGVybWluYWwgZnVuY3Rpb24KbGVhZiA8LSB0ZXJtaW5hbCh0ZXJtcyA9IGFzLmNoYXJhY3RlcihyZXZpZ28kdGVybV9JRCksIG9udG9sb2d5ID0gIlAiKQpsZW5ndGgobGVhZikKYGBgCgpgYGB7ciBmaWcuYXNwPTEsIGZpZy53aWR0aD0zfQojIHBsb3QKcCA8LSBkb3RwbG90X2dvKGdvLnBkYXQuc3ViID0gZ28ucGRhdFtnby5wZGF0JElEICVpbiUgbGVhZiwgXSkKCmdnc2F2ZSgKICBwLCAKICBmaWxlbmFtZSA9ICIuLi9yZXN1bHRzLzA0X2RlLWdlbmVzLWJ5LWNlbGx0eXBlL2xvZ2ZjXzAuNDAvZ28vcGxvdHMvZ29fZG90cGxvdF9yZXZpZ29fdGVybWluYWwtbm9kZXMucGRmIiwKICB3aWR0aCA9IDYsIGhlaWdodCA9IDUuMjUsIHVuaXRzID0gImluIgopCgpwcmludChwKQpgYGAKCgojIFNlc3Npb24gSW5mbwpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCiMgUmVmZXJlbmNlcwo=